Загрузка больших файлов
Материал из PhpWiki.
Примечание - статья несколько устарела. TODO - добавить ссылки на flash+js - аплоадеры и описание технологии.
На этой странице обсуждается целесообразность загрузки больших через браузер, используя HTTP-протокол. Даются ответы на ворпосы «Кто и как может ограничивать объём загружаемых файлов?» и «Чем плоха загрузка больших файлов при помощи HTTP-протокола?». Рассматриваются предпосылки возникновения проблемы и способы её преодоления.
Кто может ограничивать объём загружаемых файлов?
- Сам РНР. Тщательно изучите все настройки РНР, чтобы понять, как может быть ограничен оъём загружаемых файлов.
- HTTPD (к примеру, сервер Apache). Тщательно изучите все настройки Вашего сервера, чтобы понять, как Ваш сервер может ограничивать объём загружаемых файлов.
- Ваш браузер. Изучите настройки и документацию к Вашему браузеру, чтобы понять, как он может ограничивать объём загружаемых файлов. Изучите также спецификацию HTML: многие браузеры поддерживают ограничения в размере загружаемых файлов, указанные в специальных полях type="hidden".
- Прокси сервер или брандмауэр (если есть). Прокси сервера и брандмауэры могут быть настроены таким образом, чтобы ограничить объём передававемых по протоколу HTTP данных.
- Какие угодно другие программы-надстройки над перечисленными выше. Например, в комментариях пользователей можно найти упоминания о том, что объём загружаемых файлов значительно ограничивался при использовании SSL c включённой настройкой “Accept client certificates”.
Как может быть ограничен объём загружаемых файлов?
- указанием максимального размера загружаемых файлов (в основном – в РНР и в браузере). Например, в РНР это post_max_size. В браузерах это ограничение может задаваться, к примеру, указанием поля type="hidden" MAX_FILE_SIZE="xxx".
- указанием максимального размера передаваемых методом POST данных. Это размер всех загружаемых файлов, всех передаваемых методом POST переменных и всех заголовков, используемых для разделения полей в POST-запросе. В РНР эта настройка называется upload_max_filesize. В Apache это настройка LimitRequestBody.
- ограничениями на объёмы используемой памяти. К примеру, в РНР memory_limit. Также могут существовать ограничения в используемом Вами сервере. Ограничение связано с тем, что все POST-данные должны полностью находиться в памяти во время обработки (при формировании браузером и при обработке сервером). Файлы, которые Вы видите при загрузке файлов в Вашем РНР-скрипте образуются после того, как POST-данные уже обработаны сервером.
- указанием максимального времени на загрузку файла. Это неявный способ ограничить размер загружаемых файлов, о котором многие забывают. К примеру, в РНР для ограничения времени есть настройки max_input_time и http://php.net/manual/ru/ref.info.php#ini.max-execution-time max_execution_time]. В Apache это директива TimeOut.
- Если РНР у Вас установлен как CGI, то это могут быть также дополнительные настройки, связанные с тем, что HTTP сервер ограничивает время выполнения CGI-скрипта.
- Некоторые браузеры могут разрывать соединение досрочно, если в течение какого-то времени они не получают никакого ответа от сервера. А сервер не может прислать браузеру никакой ответ, пока он не получит все данные.
Чем плоха загрузка больших файлов при помощи HTTP-протокола?
- при использовании медленных модемных соединений при загрузке большого файла может разорваться связь. Докачать файл в таком случае нельзя будет. Технических возможностей для решения этой проблемы не существует.
- в большинстве FTP-браузеров загрузка файла по ФТП выполняется так же, как если бы Вы копировали файлы на локальном диске. С этой точки зрения, FTP-браузер удобнее для передачи файлов, чем HTTP-браузер.
Если в Вашем случае это не так, то это потому, и только потому, что Вы никогда не работали (или очень редко работаете) с FTP-браузерами. Выберите себе удобный FTP-браузер, пользуйтесь им постоянно, и Вы обнаружите, что качать файлы с его помощью – значительно проще, чем с помощью HTTP-браузера.
- в отличие от FTP-браузеров, большинство HTTP-браузеров не отображают ход передачи файлов, либо отображают его лишь очень приближённо. В большинстве случаев HTTP-браузеры не выводят время, оставшееся до конца передачи формы на сервер.
- увеличение объёма загружаемых данных может значительно снизить защищённость Ваших скриптов. Проблема связана с тем, что настройки объёма загружаемых данных действуют на ВСЕ загружаемые данные.
Например, если Вы увеличите объём загружаемых данных до 100 МБ и в каком-то поле в одной из форм Вашего сайта, не связанной с загрузкой файлов, а, к примеру, связанной с модификацией данных о пользователе, Вы передаёте какой-нибудь числовой идентификатор (к примеру, номер месяца дня рождения этого пользователя, который, по идее дожен быть числом от 1 до 12), который подставляете в SQL-запрос, то Вам следует приготовиться, что злой хакер передаст в этом поле не число от 1 до 12, а загрузит туда все 100 МБ. Если Вы не настроили Ваш сервер баз данных на обработку очень больших запросов (по умолчанию в My SQL это порядка 16 МБ), то такой запрос приведёт к ошибке, которую и будет эксплуатировать хакер.
- значительное увеличение времени выполнения Ваших скриптов может снизить защищённость Вашего сервера. Например, это может упростить выполнение DoS-атаки, основанной на том, что большое количество скриптов запускаются и выполняют какие-нибудь длинные бесполезные, на занимающие процессорное время задачи. Если время выполнения скриптов маленькое, то для эффективной DoS-атаки следует постоянно перезапускать скрипты (для защиты в таком случае часто используются блокировки, учитывающие количество обращений с заданного IP-адреса). Если же скрипты выполняются долго, то запустив скрипт однажды, о перезапуске скриптов можно долго не беспокоиться и простой подсчёт количества обращений становится бесполезным.
Внимание! Этот список не является списком всех возможных уязвимостей, которые могут появиться при перенастройке Ваших программ для увеличения объёма передаваемых на сервер файлов.
В связи с чем появилась проблема?
HTTP – это протокол для передачи гипертекстовых страниц. Изначально он разрабатывался для использования в режиме «только чтение», когда Вы можете только читать страницы и не можете их изменять.
Этот протокол, однако, позволяет передавать данные от пользователю серверу. Это сделано для того, чтобы сервер мог динамически генерировать страницы, в зависимости от пришедших данных. К примеру, сам Apache поддерживает возможность вывода списка файлов, позволяя пользователям пересортировывать этот список разными способами. Способ сортировки задаётся в переменной, которая передаётся методом GET.
Но передача методом GET обладает некоторыми недостатками. Например, с его помощью нельзя передавать важную, конфиденциальную информацию, так как адрес страницы (вместе со всеми переменными, которые переданы этим методом), показывается в адресной строке браузера и записывается в журналы прокси-серверов.
Особенность TCP/IP протокола (а именно этот протокол является следующим в стеке протоколов при передачи данных по HTTP) состоит в том, что в нём передаются не файлы, а пакеты. Это, кроме всего прочего, означает, что не зная длину данных невозможно сказать, когда следует завершить чтение данных. Сами данные должны быть такими, чтобы можно было сказать, когда они закончились. Например, заголовки заканчиваются по приходу символа конца строки. А длину тела запроса можно указать в загаловках (при помощи заголовка Content-Length). Спецификации большинства протоколов требуют, чтобы заголовки не были слишком длинными; как правило, максимальная длина одного заголовка ограничивается объёмом порядка от 256 байт до 8192 КБ.
Поэтому, другой недостаток метода GET связан с ограниченностью объёма передаваемых переменных. Ограничение возникло в связи с ограничением на длину одного заголовка в HTTP-запросе и в связи с отсутствием технической возможности указать реальную длину данных.
Для решения проблем, присущих методу GET, был придуман метод POST. Данные методом POST передаются не в заголовках HTTP-запроса, а в теле HTTP-запроса. Это даёт возможность, во-первых, убрать передаваемые данные из адресной строки браузера, во-вторых – указать в заголовках длину тела запроса, тем самым значительно увеличив его размер.
Поскольку HTTP позволяет передавать данные, то и сам HTML включает в себя средства для того, чтобы пользователи могли указывать эти данные. Таким средством являются формы. К примеру, когда Вы оставляете сообщение в форуме, Вы заполняете форму, и отправляете её на сервер методом POST.
Изначально спецификация HTTP вообще не предусматривала такую возможность, как передача файлов. Однако передавать файлы при помощи формы в браузере – это часто удобная возможность, поэтому была придумана отдельная спецификация – расширение HTTP, которое позволяет передавать файлы (RFC 1867).
Большинство современных людей не помнят тех времён, когда через браузер нельзя было передавать файлы на сервер, и не знают, как устроен весь процесс передачи файлов внутри. Поэтому, многим современным программистам кажется, что увеличение размера передаваемых по HTTP файлов связано только с дописыванием ноликов (для увеличения порядка чисел) в соответствующие настройки.
Когда Вы программируете под Интернет, Вам следует всегда помнить, что
- HTTP предназначен для передачи гипертекстовых страниц от сервера в браузер пользователя
- HTTP НЕ предназначен для передачи файлов от пользователя на сервер
Некоторые могут возразить, мол «Как это HTTP не предназначен для передачи файлов на сервер? Там же есть такие возможности!» На это можно ответить, что в FTP-браузер тоже можно внедрить возможность хождения по HTML-страницам, используя для загрузки страниц исключительно протокол FTP. И это даже удобно, если Вам нужно просто просмотреть содержимое какого-нибудь файла и перейти из одного файла на какой-нибудь другой файл. Но при постоянной работе обнаружится множество своих неудобств, не присущих протоколу HTTP.
Это всё потому, что:
- FTP предназначен для передачи любых файлов в любом направлении
- FTP НЕ предназначен для передачи гипертекстовых страниц
Как добиться загрузки больших файлов?
В общем случае рекомендуется не изменять параметры, ограничивающие объёмы загружаемых файлов и не загружать большие файлы через HTTP. Но если приведённые Выше доводы Вас не убедили, и если Вы знаете, что делаете, зачем, и полностью осознаёте все последствия, то Вы можете увеличить объём загружаемых файлов.
Бесконечно увеличивать объём загружаемых при помощи HTTP-протокола данных нельзя. Но можно увеличивать в некоторых разумных пределах.
Однозначного алгоритма, позволившего бы Вам увеличить объём загружаемых файлов нет, поскольку эти объёмы ограничиваются неопределённым набором используемых Вами программ неопределённым набором способов. Некоторые из этих программ и некоторые используемые ими способы ограничения объёмов файлов перечислены выше.
В первую очередь попробуйте увеличить значения настроек, ссылки на описание которых есть в разделе «Как может быть ограничен объём загружаемых файлов?».
Не забывайте, что настройки используемых Вами программ могут быть прописаны в большом количестве разных мест, при этом могут учитываться все настройки, с учётом приоритета мест.
Например, настройки РНР могут быть прописаны в файла php.ini, .htaccess, httpd.conf, а могут задаваться непосредственно в скрипте. То же время выполнения скрипта может задаваться функцией set_time_limit(). Многие настройки могут изменяться функцией ini_set().
Настройки сервера Apache могут быть прописаны в httpd.conf, но многие из них могут быть переопределены в .htaccess. Для каждого отдельного каталога действуют все .htaccess файлы, найденные во всех родительских каталогах. Поэтому, даже если в текущем каталоге нет файла .htaccess, или в нём нет изменения нужных Вам настроек, всё равно следует проверять все родительские каталоги. Более того, настройки могут быть прописаны в разных контекстах. Например, если LimitRequestBody указан в контексте <Files>, то он будет иметь более высокий приоритет, чем глобальное определение. И даже это не всё: некоторые настройки могут быть запрещены к изменению при помощи других настроек.
Следует помнить, что max_execution_time и max_input_time могут быть одинаковыми на разных серверах, но на более быстром сервере за это время большой файл успеет загрузиться, а на медленном сервере (или на том же сервере, но в моменты пиковой загрузки, когда к серверу обращается много пользователей), при тех же настройках тот же файл уже не загрузится.
Встречались также случаи, когда на компьютере хранится несколько копий файлов настроек и программист меняет один из них, а сервер загружает другой. Часто меняют настройки и забывают после этого перезагрузить сервер.
Если не поможет изменение настроек – следуйте рекомендациям раздела «Кто может ограничивать объём загружаемых файлов?», то есть изучите все настройки (а не только описанные в этой статье) во всех используемых Вами программах. Можете начать с поиска всех настроек, в которых встречается слово upload или limit. Потом посмотрите все остальные.
Если и это Вам не поможет, значит, скорее всего где-то Вы допустили ошибку: либо неправильно прописали настройки, либо Ваши настройки переопределяются в каком-то другом месте, либо их изменение запрещено другими настройками, либо о каких-то настройках не прочитали.
Не надейтесь, что здесь перечислены все настройки и все программы, которые могут ограничивать размер загружаемых файлов. Количество деталей в этом вопросе такое, что можно сказать, что
Загрузка больших файлов через HTTP – это не технология, а искусство.
Простое решение проблемы
Для загрузки больших файлов на сервер используйте FTP.
Если загруженные файлы должны как-то обрабатываться скриптом на языке РНР, это можно сделать, используя CRON для периодического запуска Ваших скриптов или по специальной кнопке в веб-интерфейсе. При запуске Ваш скрипт ищет новые загруженные через FTP файлы и обрабатывает их так, как раньше Вы хотели их обработать, загружая методом POST по HTTP. Если у Вашего локального компьютера есть выделенный IP и Вы сами закачиваете файлы на сервер
Решение, предложенное Кром.
На локальном компьютере ставится простейший ftp. На сервере прописываешь ip, логин и пароль. Если крона нет, то просто по обращению к странице на сервере проверяешь локальный ftp, закачиваешь файлы на серер, автоматически делаешь ссылки и т.д. Т.е. вообще без всякого вмешательства все работает.
Если понадобится описание файлов и какая-то дополнительная информация, то заходишь в админку и добавляешь необходимые данные. При этом на странице тебе выдается весь список закачанных файлов или тех, которые еще находятся на локальном ftp. Под ними поля для дополнительной информации, чекбоксы и т.д. Заполнил всё, нажал ОК и пошёл курить. Программа сама докачает необходимые файлы и выложит их в публичный доступ. Смотрите также
Полезные статьи по загрузке файлов на сервер
Upload файлов, и все с этим связанное
- Краткий экскурс в загрузку файлов, настройка сервера, загрузка нескольких файлов, автоматическая загрузка файлов на сервер, хранение файлов в базе данных, загрузка файлов с русскими именами, отображение статуса загрузки или progress bar, примеры скриптов.
- Документация по РНР. Chapter 38. Handling file uploads.
- RFC 1867. Form-based File Upload in HTML.
- RFC 1945. Hypertext Transfer Protocol — HTTP/1.0
- RFC 2616. Hypertext Transfer Protocol — HTTP/1.1
Программы, которые могут Вам помочь
File Upload Applet
Mega Upload Progress Bar
Благодарности
Кром. Способ загрузки больших файлов, если у Вашего локального компьютера есть выделенный IP и Вы сами закачиваете файлы на сервер.
Автор: Yuri Popov. Copyright http://phpclub.ru/faq/