Всем кому приходилось реализовать на PHP загрузку файлов на сервер с отображением "реального" прогресс бара знают, что задача не из простых. Для создания полноценного прогресс бара загрузки файлов приходилось использовать сторонние расширения, такие как APC или uploadprogress. С выходом PHP 5.4 ситуация в корне изменилась. В PHP был встроен механизм позволяющий контролировать процесс загрузки файлов. Данное решение было добавлено в механизм сессий. Теперь, когда на сервер загружаются файлы, информация о процессе загрузки сохраняется в текущей сессии. Данная информация может быть извлечена и использована для вывода прогресс бара загрузки и дополнительных данных, например, процент загрузки и/или примерное время до окончания процесса.
Параметры настройки
Для настройки данного функционала доступно несколько конфигурационных параметров:
session.upload_progress.enabled
[=1] - Включает/выключает данный функционалsession.upload_progress.cleanup
[=1] - Включает/выключает очистку данных из сессии после завершения загрузкиsession.upload_progress.prefix
[=upload_progress] - Префикс переменной в сессииsession.upload_progress.name
[=PHP_SESSION_UPLOAD_PROGRESS] - Название элемента массива $POST в котором передается название переменной сессии, в которой будет сохранена информация о загружаемых файлах. Если в массиве $POST не будет задана переменная с указанным названием, то данная функциональность не будет доступнаsession.upload_progress.freq
[=1%] - Частота обновления данных в сессии, данные будут обновляться при получении каждыхsession.upload_progress.freq
байт Если за значением следует '%', то значение будет интерпретировано как процент от общего размера загружаемых данныхsession.upload_progress.min_freq
[=1.0] - Минимальная задержка в секундах между обновлениями данных
Информация о процессе загрузи
Прогресс загрузки будет доступен когда включен параметр session.upload_progress.enabled
, когда массив $POST содержит элемент с названием session.upload_progress.name
и значение данного элемента не пустое.
При выполнении данных условий в текущей сессии будет создана переменная, название которой будет сформировано из значения параметра session.upload_progress.prefix
и значения элемента с названием session.upload_progress.name
массива $POST:
$_SESSION[ini_get(session.upload_progress.prefix).$_POST[ini_get(session.upload_progress.name)]]
В эту переменную будет сохранена информация о процессе загрузи и загружаемых файлах в виде ассоциативного массива со следующей структурой:
$_SESSION[ini_get(session.upload_progress.prefix).$_POST[ini_get(session.upload_progress.name)]] = array(
'start_time' => дата запроса,
'content_length' => размер данных,
'bytes_processed' => кол-во принятых и обработанных байт,
'done' => флаг завершения загрузки всех данных(true/false),
// Информация о файлах
'files' => array(
// Информация о первом файле
0 => array(
'field_name' => название поля в форме,
'name' => имя файла,
'tmp_name' => имя файла во временном каталоге,
'error' => код ошибки,
'done' => флаг завершения загрузки данного файла(true/false),
'start_time' => время начала загрузки данного файла,
'bytes_processed' => кол-во принятых и обработанных байт данного файла,
),
// Информация о втором файле
1 => array(
…
),
n => array(
…
),
)
);
Простая форма:
<form action="" method="POST" enctype="multipart/form-data">
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="test" />
<label for"file1">Файл 1: </label><input type="file" name="file1" />
<label for"file2">Файл 2: </label><input type="file" name="file2" />
<label for"file3">Файл 3: </label><input type="file" name="file3" />
<input type="submit" value="Загрузить" />
</form>
При загрузке файлов с помощью этой формы в сессии будет сохранена информация:
$_SESSION['upload_progress_test'] = array(5) {
"start_time" => 1311502890,
"content_length" => 485856963,
"bytes_processed" => 485856963,
"done" => true,
"files" => array(3) {
[0] => array(7) {
"field_name" => "file1",
"name" => "test_file1.pdf",
"tmp_name" => "/tmp/phpky5BVc",
"error" => 0,
"done" => true,
"start_time" => 1311502890,
"bytes_processed" => 335026
},
[1] => array(7) {
"field_name" => "file2",
"name"=> "test_file2.avi",
"tmp_name"=> "/tmp/phpGVvw2W",
"error" => 0,
"done"]=> true,
"start_time" => 1311502890,
"bytes_processed" => 435904512
},
[2] => array(7) {
"field_name" => "file3",
"name" => "test_file3.doc",
"tmp_name" => "/tmp/php6BVPAq",
"error" => 0,
"done" => true,
"start_time" => 1311502922,
"bytes_processed" => 49616655
}
}
}
Отмена загрузки
Процесс загрузки можно отметить в любой момент, для этого нужно установить значение TRUE
элементу cancel_upload
массива данных в сессии:
$_SESSION['upload_progress_test']['cancel_upload'] = true;
Это приведет к отмене загрузки файлов и при этом рекурсивно, элементу error
, в массивах данных о файлах, будет установлено значение UPLOAD_ERR_EXTENSION
Рабочий пример
Посмотрим как это работает на простом примере, реализовав простую загрузку файлов на сервер. Файлы будут отправляться на сервер через AJAX с помощью плагина form для jQuery. Данные о процессе загрузки будем запрашивать дополнительными запросами к серверу через AJAX, каждые пол секунды. На основе полученных данных будет заполнять прогресс бар
Страница загрузки файлов (index.php):
<?php
session_start();
?>
<html>
<head>
<script type="text/javascript" src="jquery-1.6.2.min.js"></script>
<script type="text/javascript" src="jquery-ui-1.8.14.custom.min.js"></script>
<script type="text/javascript" src="jquery.form.js"></script>
<link href="jquery-ui-1.8.14.custom.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.ui-progressbar-value {
background-image:
url(images/pbar-ani.gif);
padding-left:10px;
font-weight:normal;
}
#upload_form {
display:block;
}
#progress {
display: none;
}
#progress #bar {
height: 22px;
width: 300px;
}
</style>
<script type="text/javascript">
var t;
/* Функция получения информации о процессе загрузки по AJAX */
progress = function(){
$.ajax({
url: 'upload.php',
dataType: 'json',
success: function(data){
if(data.percent) {
$("#bar").progressbar({
value: Math.ceil(data.percent), // Заполняем прогресс бар
});
$('.ui-progressbar-value').text(data.percent+'%'); // Отображаем на прогресс баре процент загрузки
}
}
});
}
$(document).ready(function() {
/* Отправка формы загрузки по AJAX */
$('#form').ajaxForm({
type: 'POST',
success: function() {
clearTimeout(t);
$('#progress').html('<b>Файл был загружен!</b>');
},
beforeSubmit: function() {
/* Перед отправкой данных на сервер прячем форму, показываем прогресс бар и запускаем таймер */
$('#upload_form').hide();
$('#progress').show();
t = setInterval("progress()", 10);
}
});
/* Отправка запроса на отмену загрузки */
$('#cancel-form').ajaxForm({
success: function() {
clearTimeout(t);
$('#progress').html('<b>Загрузка была отменена!</b>');
}
});
});
</script>
</head>
<body>
<div id="upload">
<div id="upload_form">
<!-- Форма загрузки файлов на сервер -->
<form id="form" action="upload.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="test" />
<label for="file1">Файл для загрузки 1: </label><input type="file" name="file1" /><br /><br />
<label for="file2">Файл для загрузки 2: </label><input type="file" name="file2" /><br /><br />
<label for="file3">Файл для загрузки 3: </label><input type="file" name="file3" /><br /><br />
<input type="submit" value="Загрузить" />
</form>
</div>
<div id="progress">
Загрузка файлов<br /><br />
<!-- Прогресс бар -->
<div id="bar"></div><br />
<!-- Форма отмены загрузки файлов -->
<form id="cancel-form" action="cancel.php" method="POST" enctype="multipart/form-data">
<input type="submit" value="Отмена" />
</form>
</div>
</div>
</body>
</html>
Сценарий (upload.php):
<?php
/*
* Здесь все просто, берем данные о процессе загрузки из сессии
* и возвращаем их в JSON формате
*/
session_start();
$percent = 0;
$data = array();
if(isset($_SESSION['upload_progress_test']) and is_array($_SESSION['upload_progress_test'])) {
$percent = ($_SESSION['upload_progress_test']['bytes_processed'] * 100 ) / $_SESSION['upload_progress_test']['content_length'];
$percent = round($percent,2);
$data = array(
'percent' => $percent,
'content_length' => $_SESSION['upload_progress_test']['content_length'],
'bytes_processed' => $_SESSION['upload_progress_test']['bytes_processed']
);
}
echo json_encode($data);
?>
Сценарий отмены загрузки файлов (cancel.php):
<?php
session_start();
$_SESSION['upload_progress_test']['cancel_upload'] = true;
?>