Как сделать на сайте загрузку больших файлов на сервер с индикацией количества загруженных мегабайт (прогрессом)

 

Некоторые владельцы сайтов при создании страницы для загрузки больших файлов на сервер (например, видео, аудио)  сталкиваются с серьёзной проблемой - файлы загружаются довольно долго! Кроме того, что бы пользователь понимал что происходит, не помешает сделать индикацию загрузки в виде полосы прогресса и строки, показывающей количество загруженных мегабайт. Попробуем максимально подробно разобраться, как это можно реализовать.

Инструмент XMLHttpRequest

Для загрузки файлов есть очень хороший инструмент, встроенный в браузер - XMLHttpRequest. Это объект, API, при помощи которого создаются и передаются запросы от браузера к серверу и обратно, т.е. браузер может создавать запрос и получать на него ответ от сервера; самое важное состоит в том, что это делается без перезагрузки страницы. В нашем случае XMLHttpRequest используется для:

  • отправки различных данных (файлы, сопроводительный и прочий текст к ним и т.д.) на сервер, причём происходит это на весьма хорошей скорости (например, 650 Мб будет передано приблизительно за 40-50 сек);
  • периодической отправки запросов и получения ответа об объёме загруженного файла, что будет использовано для управления индикацией загрузки.

 Обратите внимание: в коде используется HTML 5.0.

Схема загрузки файлов на сервер

Обычно на страницу, кроме заголовка и поля для ввода загружаемого файла, выводятся и текстовые поля, в которых пользователь, загружающий фото, видео или что-то ещё на ваш сайт, может указать название, написать какой-то сопроводительный текст и так далее. На странице загрузки следует использовать две формы отправки (одна для отправки большого файла, другая - текстовой информации) и скрипт, поочерёдно запускающий и контролирующий ход процесса. Почему это удобно станет ясно чуть позже, а пока рассмотрим общую схему.

 

 

Как видно из схемы, управляющий скрипт сначала запускает передачу файла (стрелка 1) из формы № 2 обработчику, который проверяет файл и загружает его в соответствующую папку (стрелка 2); одновременно с этим браузер при помощи XMLHttpRequest периодически обменивается с сервером данными о загрузке: посылает запросы и получает ответы (стрелка 3) о количестве загруженных байт, на основании чего выводит данные в прогресс-бар.

Как только загрузка будет успешно завершена (в противном случае процесс остановится и будет выведено сообщение), управляющий скрипт даст команду на отправку данных из формы № 1 другому обработчику (стрелка 4), который проверяет все данные, заносит в базу (стрелка 5) и по окончании, если всё прошло без ошибок, выводит страницу подтверждения загрузки (стрелка 6). 

 

Код для ввода и передачи данных с выводом прогресса на страницу

Теперь рассмотрим конкретный код страницы; представим себе, что это страница, позволяющая пользователю загружать небольшие видеофайлы на сервер, размером до 880 Мб, т.е. 922746880 байт (1 Мб = 1048576 байт).

Каким образом размещать управляющий скрипт - решать вам: можно поместить его в отдельный файл и подключать при запуске, а можно просто разместить в конце кода страницы; именно второй вариант мы и используем в качестве примера.

Сначала вставим на страницу заголовок и создадим форму № 1:

 

 

Как обычно, в начале страницы идёт заголовок, далее стандартная форма, содержащая поле для ввода названия (input) и поле для ввода сопроводительного текста (textarea); при необходимости можно создать и любые другие поля.

Теперь создадим форму № 2, отвечающую за загрузку "тяжёлого" (т.е. большого по размеру) файла, вывод индикации загрузки и запуск всего процесса в целом.

 

Это обычная, стандартная форма для передачи данных методом POST, для которой указаны атрибуты id для идентификации управляющим скриптом, class для таблиц CSS, и указан адрес обработчика данной формы.

 Обратите внимание: обычно теги, независимо от длины, в коде пишутся в одну строку; в этой статье некоторые теги пишутся на нескольких строках исключительно для удобства их восприятия.

Далее следует скрытое поле и поле ввода видеофайла с сопроводительной информацией:

Скрытое поле информирует систему о максимально допустимом размере файла; при превышении будет выведено предупреждение и загрузка приостановлена. Поле для ввода видеофайла имеет стандартные атрибуты, в том числе и required - атрибут, не позволяющий отправить "пустое" поле. Далее следует текст, подсказывающий пользователю максимально допустимый размер файла.

Следующая часть кода выводит индикацию загрузки файла:

Сначала идёт информационный заголовок, затем строка прогресса, которая выглядит в каждом браузере по-разному и зависит от настроек CSS. Далее следует текстовый блок, выводящий данные в процентах, а поле загрузки фразу "Загружено!".

И последней идёт строка, появляющаяся в самом конце загрузки с фразой "Подождите..."; это важно для больших файлов, потому, что по окончании требуется ещё несколько секунд, что бы сервер выполнил все поставленные задачи и выдал соответствующий ответ (подробно об этом будет сказано ниже).

Закрывает страницу, точнее, видимую её часть, кнопка отправки данных на сервер:

 

Здесь ничего сложного нет - обычная кнопка для отправки формы; при желании вместо input можно использовать тег button, что позволит сделать кнопку с изображением.

Код управляющего скрипта

Как было сказано ранее, в нашем варианте данный скрипт будет расположен прямо на странице: это упрощает работу с кодом и не требует создавать дополнительные файлы и папки, тем более, что в этом совершенно нет необходимости: данный скрипт может быть использован только с этой страницей.

В начале создаются константы, которым при помощи document.getElementById() присваиваются ссылки на определённые элементы страницы, определяемые по аргументу id этих объектов:

 

 

В этой части кода проверяется допустимый размер: если он окажется более 850 Мб, то будет выведено соответствующее предупреждение:

 

 

Далее создаётся форма и отправляется на сервер методом POST:

Константа formSent получает объект, представляющий данные HTML-формы, константа xhr - новый объект XMLHttpRequest, предоставляющий возможность передачи данных на сервер. Далее, если файл выбран, в форму добавляется файл (formSent.append), указывается обработчик (прослушиватель) событий 'progress', loadingIndicator, отвечающий за работу прогресса, и обработчик событий 'load' loadHandler - функция, запускаемая по окончании загрузки файла.

В конце созданный объект XMLHttpRequest инициализируется (xhr.open) и при помощи метода send отправляется запрос, т.е. форма отправляется на сервер. Обратите внимание, что в качестве адреса страницы, к которой отправляется запрос, указывается адрес (не путь на сервере, а именно адрес!) обработчика; например, для CMS Joomla данный адрес может выглядеть вот так: 

 При получении данного запроса система переправит его на необходимый скрипт, в рассматриваемом выше примере 0 обработчику № 2, действие под стрелкой 1.

Далее в коде расположены две функции:

Эта функция выполняет роль прослушивателя ответов сервера, на основании которых подсчитывает количество загруженных мегабайт, пересчитывает их на проценты и выдаёт обе величины в текстовые поля videoForm_sz и videoForm_st, представленные в скрипте константами sizeText и statusText соответственно. Так же данная функция управляет ещё одним элементом страницы - прогрессбаром.

В самом конце при помощи конструкции if в в поле p_infoText, представленное здесь константой infoText, выводится фраза "Подождите, идёт обработка данных...". Дело в том, что после полной загрузки обработчику требуется ещё несколько секунд для завершения всех действий, в данном случае - окончания установки файла в заданную папку сайта - именно об этом и информирует данная строка. 

Как только все действия будут завершены и скрипт получит от этом уведомление от сервера (см. схему, стрелка 3), запустится функция loadhandler, задача которой запустить передачу на сервер формы № 1, т.е., по сути данная функция выполняет роль "нажимаетмой" кнопки с атрибутом submit, в результате чего  всё содержимое текстовых полей будет так же передано на сервер обработчику 1 (см. схему, стрелка 4):

Обработчик внесёт все полученные сведения в базу данных и откроет пользователю страницу с подтверждением удачной загрузки. Как всё это происходит рассмотрим ниже.

Обработчик 2: действия при загрузке файла на сервер

Итак, браузер пользователя ("клиент") отправил запрос, который сайт переадресовал соответствующему  обработчику, в нашем случае №2; основная задача, которая должна быть им выполнена - загрузка файла в указанную папку. Некоторая часть кода зависит полностью от применяемой на сайте CMS, но основная часть одинакова и выглядит так:

Как известно, ассоциативный массив $_FILES содержит несколько переменных относящихся к файлу, поэтому для проверки наличия файла достаточно проверить одну их них, в данном случае tmp_name; если она не NULL, то выполняется следующее действие. 

В следующем действии если функция is_uploaded_file, проверяющая метод загрузки файла, вернёт положительный результат, т.е. загрузка прошла именно через HTTP POST, то запускается последнее, самое главное действие - функция move_uploaded_file перемещает загруженный файл в указанное место на сайте, в данном случае в папку видео  (PATH - полный путь к данной папке на сервере), однако вы можете указать любое другое место на своё усмотрение.

Как только загрузка будет завершена, браузеру будет выдано уведомление (см. схему, стрелка 3) и управляющий скрипт запустит обработчик формы № 1.

Обработчик 1: загрузка данных из текстовых полей

Эти данные помещаются в обычную базу данных; наличие определённых столбцов зависит от того, как вы будете в дальнейшем использовать загруженные файлы, но основную информацию необходимо сохранять, например о:

  • новом имени файла (если менялось);
  • старом (оригинальном) имени файла;
  • времени и дате загрузки;
  • авторе файла

и так далее. Обратите внимание на первые два пункта в этом списке: если вы собираетесь получать много различных файлов от разных пользователей, то при сохранении на сайте эти файлы необходимо переименовывать, например, в file_number_1,  file_number_2,  file_number_3 и так далее; это позволит избежать проблемы с загрузкой файла, имя которого случайно совпало с именем уже загруженного ранее. Для реализации системы переименования нужно добавить в код всего одну строку, в которой id (номер) создаваемой строки базы данных (он же - номер файла) будет приплюсовываться к file_number_, после чего новое имя файла нужно указать в аргументах функции move_uploaded_file (во втором аргументе вместо старого имени из массива $_FILES.

Так как код, используемый для работы с базами данных у разных CMS различается, то в рамках данной статьи рассматривать его нет смысла - за этим стоит обратиться к документации к системе управления сайтом.

В конце обработчика можно добавить код, автоматически направляющий пользователя на страницу с подтверждением удачной загрузки или наоборот, с указанием ошибки; для этого можно использовать обычную функцию header() с указанием соответствующего адреса (адресов). В этом случае приведённый код будет выглядеть приблизительно так:

 Вместо фразы "страница..." вставьте указанную функцию с указанием соответствующего адреса.

Всё! Вам осталось разместить код на сайте, создать дополнительные строки в таблицах CSS для оформления созданных элементов и можно запускать всю схему в работу!