Pull to refresh

Загрузка файлов с помощью HTML5 и сколько раз мы сказали нехорошие слова

Reading time3 min
Views7.2K
Стояла задача: найти или создать загрузчик файлов на сервер, использующий возможности HTML5 для мультизагрузки. Загрузчик должен:
  • отправлять методом POST любые параметры вместе с файлом;
  • отправлять куки;
  • предоставлять возможность выбора сразу нескольких файлов (или нескольких тысяч – тут как пользователь захочет);
  • отправлять файлы группами;
  • файлы собираются в группы до определенного количества мегабайт, или до определенного количества файлов в группе; (это связано с тем, что на сервере есть ограничение на размер POST запроса и на количество файлов в одном пакете)

Именно по причине отсутствия опыта работы с флешем, для создания загрузчика был выбран только JavaScript.

Итак, что из этого получилось


Сначала был поиск: SWFupload, Uploadify и другие были изучены. Возможно, не слишком хорошо, но каждый из них не удовлетворял всем требованиям.

Баги, баги, баги. Некоторые версии Flash не могут управлять именем переменной. SWFupload не отправляет куки, но у него есть сборщик куков, и он их отправляет только методами GET или POST. Однако у нас CMS на сервере проверяет сессию пользователя именно на основании кук, поэтому Flash был отброшен и было выбрано решение, с использованием HTML5.
Поскольку FileApi поддерживается только FF 3.0+, Chrome и Safari 4+, то разберем тонкости работы с этими браузерами.

Тонкость №1

Получение содержимого файла в Chrome с помощью readAsBinaryString.
При использовании данного метода мы столкнулись с непонятной проблемой, что все файлы приходят битыми, размер которых примерно в 1,5 раза больше изначального размера. Победить эту проблему не получилось, поэтому для хрома ( а так же сафари) мы воспользовались FormData.

Чтобы отправить группу файлов + какие-то данные методом POST в FF (до 4.0) формируем multipart/form-data:
Content-Type: multipart/form-data; boundary=------multipartformboundary1295790618
\r\n
--------multipartformboundary1295790618
\r\n
Content-Disposition: form-data; name='user_files[]';filename='My_File1.jpg'
\r\n
Content-Type: application/octet-stream
\r\n
\r\n
содержимое файла
\r\n
--------multipartformboundary1295790618
\r\n
Content-Disposition: form-data; name='user_files[]';filename='My_File2.jpg'
\r\n
Content-Type: application/octet-stream
\r\n
\r\n
содержимое файла
\r\n
--------multipartformboundary1295790618
\r\n
Content-Disposition: form-data; name='my_param'
\r\n
\r\n
Param_value
\r\n
--------multipartformboundary1295790618
\r\n
Content-Disposition: form-data; name='my_param'
\r\n
\r\n
Param_value
\r\n
--------multipartformboundary1295790618--
\r\n


Тонкость №2

Определение поддержки FormData можно проверять следующим образом:
function isFormDataSupported() {
return (window.FormData);
}


Тонкость №3

В FF 3.0/3.5 и FF 3.6 используются разные функции для получения содержимого файлов:
FF 3.0/3.5: readAsBinary()
FF 3.6: getAsBinary()

Тонкость №4

Также есть разница в отправке файлов через XMLHttpRequest:
для FormData нужно отправлять методом send(), а для отправки файлов, полученных методом getAsBinary() или с помощью readAsBinaryString необходимо отправлять методом sendAsBinary().

Тонкость №5

FF редко вызывает событие progress у XMLHttpRequest, в отличии от хрома или Сафари. На деле у FF событие progress вообще иногда не отрабатывало при очень быстром соединении или на локалхосте.

Тонкость №6

При событии abort у XMLHttpRequest в FF также вызывается событие error. С этим нужно быть особо внимательным, если у вас там стоит обработчик на ошибки — это может вызвать дополнительные вопросы у пользователя.

Тонкость №7

Для PHP советую обратить внимание на следующие настройки в php.ini:

post_max_size — максимальный размер POST.
upload_max_filesize — максимальный размер файла.
max_file_uploads — максимальное количество загружаемых файлов.

Если будете использовать наш скрипт, не забудьте изменить скрипт под настройки своего сервера.

Демо для загрузки изображений (стоит фильтр по расширениям)
Скачать демо
Хорошая презентация на тему AJAX загрузки

UPDATE:

Тонкость №8 (спасибо soltpain за то, что заметил этот баг)

В Windows есть еще один нюанс в выборе множества файлов через [input type=file multiple]:
Количество доступных файлов для выбора зависит от общей длины имен выбранных файлов. Для хрома — 256 символов, для FF — 4096 (длина не одного файла, а общая длина имен, то есть если есть файлы, у которых имя состоит из 20 символов, то в хроме можно выбрать максимум 10 файлов). В линуксе все нормально.

Описание бага:
codereview.chromium.org/4198004
Tags:
Hubs:
+81
Comments67

Articles

Change theme settings