Проблема, которая преследует веб-разработчиков в течение многих лет, заключается в том, как добавлять информацию в реальном времени в свои приложения, например индикатор выполнения для загрузки файлов. Пользователи нетерпеливы; они не хотят сидеть и ждать, пока браузер что-то делает, и гадают, не зависло ли оно или у них медленное соединение. Предоставление индикатора прогресса дает пользователям полезную информацию и позволяет им точно знать, что происходит.
Сначала вы можете подумать, что выполнить это можно легко, сначала получив размер файла с компьютера пользователя, а затем выполнив несколько простых вычислений для каталога на сервере, куда загружается файл. Если подумать, вы обнаружите, что все не так просто.
JavaScript может получить доступ к имени файла, типу и даже ширине и высоте локального изображения, но только в HTML5 он смог получить доступ к размеру файла . К сожалению, HTML5 все еще не является законченным стандартом и не поддерживается одинаково во всех браузерах. Альтернативным решением является использование плагина Flash, Java или ActiveX; нет, спасибо, я сдам. Еще одно решение — установить расширение Alternative PHP Cache , но оно может быть недоступно в зависимости от среды вашего хостинга, и это кажется излишним для такой небольшой задачи, как эта.
Казалось бы, все варианты чреваты неприятностями, и задача быстро стала головной болью. Но, по словам Йоды, «нет… есть другое».
Одна из многих причин, по которым я люблю PHP, заключается в том, что он облегчает, казалось бы, сложные задачи. Начиная с PHP 5.4, они сделали это снова с новым набором директив конфигурации, session.upload_progress
.
В этой статье я покажу вам, как эту функцию можно использовать для создания простого индикатора выполнения загрузки без каких-либо внешних библиотек или зависимостей браузера. Сначала я расскажу, как это работает, а затем проведу вас через создание четырех файлов, необходимых для выполнения задачи (форма загрузки, немного JavaScript, немного CSS и файл для возврата статуса загрузки).
Процесс загрузки сеанса
Помимо обычных требований разрешить загрузку файлов, есть еще два, чтобы отслеживать прогресс. Директива session.upload_progress.enabled
должна быть включена, и в вашей веб-форме должно быть скрытое поле с именем, указанным в директиве session.upload_progress.name
. Если для session.upload_progress.enabled
true (как по умолчанию в PHP 5.4 и, вероятно, за его пределами) и во время загрузки отправляется $_POST[ session.upload_progress.name ]
, информация о передаче файла становится доступной в $_SESSION
выражении $_SESSION
массив.
Вывод print_r()
массива $_SESSION
будет выглядеть примерно так во время передачи файла:
массив ( [upload_progress_myForm] => Массив ( [start_time] => 1323733740 [content_length] => 721127769 [bytes_processed] => 263178326 [сделано] => [files] => Array ( [0] => Массив ( [field_name] => userfile [name] => ubuntu-10.04.3-desktop-i386.iso [tmp_name] => [ошибка] => 0 [сделано] => [start_time] => 1323733740 [bytes_processed] => 263178026 ) ) ) )
Когда вы разрабатываете локально или в быстрой сети и загружаете небольшие файлы, вы не сможете визуально наблюдать за прогрессом, потому что передача происходит так быстро. В этом случае вы можете попробовать перенести большой файл. Убедитесь, что настройки в вашем файле php.ini
разрешают большие загрузки, в частности директивы post_max_size
и upload_max_filesize
, а затем убедитесь, что они являются нормальными значениями, когда вы приступаете к работе.
Создать форму
Первый файл, который я представлю, это форма загрузки. Просто для простоты, пример будет публиковать для себя и обрабатывать только один файл за раз. Кроме того, я не буду беспокоиться о сохранении файла после его загрузки.
Вот код для form.php
:
<?php if ($_SERVER["REQUEST_METHOD"] == "POST" && !empty($_FILES["userfile"])) { // move_uploaded_file() } ?> <html> <head> <title>File Upload Progress Bar</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <div id="bar_blank"> <div id="bar_color"></div> </div> <div id="status"></div> <form action="<?php echo $_SERVER["PHP_SELF"]; ?>" method="POST" id="myForm" enctype="multipart/form-data" target="hidden_iframe"> <input type="hidden" value="myForm" name="<?php echo ini_get("session.upload_progress.name"); ?>"> <input type="file" name="userfile"><br> <input type="submit" value="Start Upload"> </form> <iframe id="hidden_iframe" name="hidden_iframe" src="about:blank"></iframe> <script type="text/javascript" src="script.js"></script> </body> </html>
В этом примере код для фактической обработки файла был опущен для упрощения работы. Если вас интересует, как должен выглядеть такой код, ознакомьтесь со статьей « Загрузка файлов с помощью PHP » Тимоти Борончика.
После раздела заголовка, в котором содержится заголовок страницы и таблица стилей, вы увидите небольшую коллекцию элементов div. Div с идентификатором «bar_blank» является контейнером для индикатора выполнения. Div с идентификатором «bar_color» будет динамически обновляться по мере загрузки файла. Div «status» будет отображать числовое значение загруженного процента.
Форма настроена на отправку по тому же URL, а ее целевой атрибут указывает на скрытый элемент iframe. Отправка формы в скрытый фрейм позволяет удерживать посетителя на той же странице, пока работа выполняется в фоновом режиме. На самом деле, это обычная практика при «загрузке Ajax-файла», так как невозможно отправить содержимое файла напрямую с помощью объекта JavaScript XmlHttpRequest
.
Внутри формы появляется специальное скрытое поле, необходимое для заполнения массива $_SESSION
, за которым следует кнопка ввода и отправки файла для загрузки. Отправка формы вызовет функцию JavaScript с именем startUpload()
которая будет определяться включенным файлом JavaScript.
Внизу страницы находится скрытый фрейм, в котором будет размещена форма, и импорт файла script.js
.
Добавить стиль
Следующий файл style.css
довольно прост. Я определил размер контейнера индикатора выполнения и присвоил ему черную рамку размером 1 пиксель, цвет индикатора выполнения при загрузке, а также iframe и индикатор выполнения скрыты.
#bar_blank { border: solid 1px #000; height: 20px; width: 300px; } #bar_color { background-color: #006666; height: 20px; width: 0px; } #bar_blank, #hidden_iframe { display: none; }
Функциональность на стороне клиента
Файл script.js
является самым большим из группы файлов. Он содержит шесть функций, которые я расскажу ниже. Многим нравится использовать jQuery для предоставления некоторых функций здесь, и вы, безусловно, можете сделать это, если хотите, но я лично предпочитаю подход старой школы. Подобно тому, как японцы придают большее значение товарам ручной работы, я просто чувствую большую страсть к коду, если он мой.
function toggleBarVisibility() { var e = document.getElementById("bar_blank"); e.style.display = (e.style.display == "block") ? "none" : "block"; } function createRequestObject() { var http; if (navigator.appName == "Microsoft Internet Explorer") { http = new ActiveXObject("Microsoft.XMLHTTP"); } else { http = new XMLHttpRequest(); } return http; } function sendRequest() { var http = createRequestObject(); http.open("GET", "progress.php"); http.onreadystatechange = function () { handleResponse(http); }; http.send(null); } function handleResponse(http) { var response; if (http.readyState == 4) { response = http.responseText; document.getElementById("bar_color").style.width = response + "%"; document.getElementById("status").innerHTML = response + "%"; if (response < 100) { setTimeout("sendRequest()", 1000); } else { toggleBarVisibility(); document.getElementById("status").innerHTML = "Done."; } } } function startUpload() { toggleBarVisibility(); setTimeout("sendRequest()", 1000); } (function () { document.getElementById("myForm").onsubmit = startUpload; })();
Функция toggleBarVisibility()
устанавливает соответствующий стиль в div «bar_blank» для отображения или скрытия индикатора выполнения по мере необходимости. Первоначально он начинается скрытно, но будет отображаться после начала загрузки и снова скрываться после завершения загрузки.
Функция createRequestObject()
создает объект XMLHttpRequest
или ActiveXObject
на основе браузера пользователя. Вероятно, это функция, которую большинство людей хотели бы использовать в jQuery или какой-либо другой среде JavaScript.
Функция sendRequest()
запрашивает файл progress.php
с помощью запроса GET, а затем вызывает handleResponse()
для обработки возвращаемых данных.
Функция handleResponse()
обрабатывает ответ от файла progress.php
который будет иметь значение от 1 до 100 в зависимости от процесса загрузки файла. Я также обновляю div «status» соответствующим значением. Если текущий процент ниже 100, то я вызываю встроенную в JavaScript функцию setTimeout()
чтобы через 1 секунду отправить другой запрос на обновление (вы можете изменить это значение соответствующим образом), в противном случае я снова скрываю индикатор выполнения и устанавливаю статус в «Выполнено.»
Функция startUpload()
делает панель загрузки видимой и отправляет запрос на обновление после задержки в 1 секунду. Эта небольшая задержка необходима для того, чтобы начать загрузку.
Последняя функция — это startUpload()
анонимная функция, которая регистрирует startUpload()
с событием отправки формы.
Прогресс в реальном времени
Последний файл, который объединяет все, — это файл progress.php
:
<?php session_start(); $key = ini_get("session.upload_progress.prefix") . "myForm"; if (!empty($_SESSION[$key])) { $current = $_SESSION[$key]["bytes_processed"]; $total = $_SESSION[$key]["content_length"]; echo $current < $total ? ceil($current / $total * 100) : 100; } else { echo 100; }
Сценарий выполняет простую математическую обработку текущего количества переданных байтов, разделенного на общий размер файла, умноженного на 100 и округленного до процента.
Информация о передаче основывается на конкатенации значения директивы session.upload_progress.prefix и скрытого значения поля session.upload_progress.name
. Поскольку моя форма прошла «myForm», ключ сеанса определяется с помощью ini_get("session.upload_progress.prefix") . "myForm"
ini_get("session.upload_progress.prefix") . "myForm"
.
Вот скриншот индикатора выполнения в действии:
Тонкая настройка поведения
PHP предоставляет некоторые дополнительные директивы, чтобы помочь точно настроить поведение загрузки сеанса, о которой вы должны знать. Например, session.upload_progress.cleanup
, для которого по умолчанию установлено значение 1, очищает дополнительные данные сеанса сразу после завершения загрузки. Вы должны быть осторожны, чтобы избежать потенциального состояния гонки.
Посмотрите еще раз на код в progress.php
и вы заметите, что я проверяю, является ли $_SESSION[$key]
пустым или нет, прежде чем продолжить. Мои функции JavaScript отключаются каждую секунду до тех пор, пока результат, полученный из progress.php
будет меньше 100. Если включен session.upload_progress.cleanup
и мой скрипт извлекает 99% загрузки, а через 1/2 секунды загрузка завершается $_SESSION[$key]
не будет существовать для следующей проверки. Если я не учту это, тогда моя функция JavaScript может продолжать работать даже после завершения загрузки.
Еще две директивы: session.upload_progress.freq
и session.upload_progress.min_freq
которые определяют, как часто следует обновлять сеанс. Значение freq может быть задано либо в байтах (т.е. 100), либо в процентах от общего количества байтов (т.е. 2%). Значение min_freq
дается в секундах и указывает минимальное количество секунд между обновлениями. Очевидно, что если min_freq
был настроен на обновление каждую 1 секунду, для вашего JavaScript было бы бессмысленно проверять каждые 100 миллисекунд.
Резюме
Теперь у вас должно быть четкое представление о том, как создать индикатор выполнения для загрузки файлов с помощью функции выполнения загрузки сеанса. В дальнейшем я рекомендую вам поэкспериментировать с загрузкой нескольких файлов, давая возможность отменить $_SESSION[$key]["cancel_upload"]
загрузку, используя $_SESSION[$key]["cancel_upload"]
или любые другие идеи, которые ваш разум может придумать. Пожалуйста, поделитесь в комментариях своим опытом и улучшениями, которые вы сделали для других, чтобы извлечь из этого пользу!
Изображение через файл 404 / Shutterstock