Php загрузка на сервер больших файлов

Закачка больших файлов или Как обойти ограничения дешевого виртуального хостинга

Однажды в очередной раз возникла задача о закачке относительно больших файлов. Говоря конкретно, клиент захотел заливать на сайт через админку видеоролики размером 20-40 мегабайт. Казалось бы, в наше просвещенное время подобный размер — это такая мелочь, о коей и говорить стыдно. Но внезапно все уперлось в настройки виртуального хостинга. Мы с ужасом обнаружили, что максимальный размер закачиваемого файла — 2M, и поменять эту цифру нет возможности. И менять хостинг по ряду причин нельзя — по крайней мере не сейчас.

Перед нами встает задача — обойти ограничения убогого виртуального хостинга. Сам принцип такого обхода очевиден: файл надо порезать на куски, залить частями, а на стороне сервера собрать в единое целое. Но делать это надо не вручную — пользователь должен выбрать файл и нажать на кнопку «Отправить». Как же это сделать?

Самая первая наша реакция — посмотреть возможности различных флеш-аплоадеров. Ведь не может быть, чтобы мировая техническая мысль не реализовала такой полезной вещи, как загрузка файла по частям. Перебираем последовательно Uploadify, SWFUpload, FancyUpload, jqUploader, jquery-transmit. Но все тщетно. Искомой фичи мы не видим. Вполне вероятно, что надо копать дальше, но время поджимает, и надо уже что-то делать…

Вышеописанное печально. Однако нам на руку играет тот факт, что это админка. Т.е. нам вовсе не нужно ориентироваться на кроссбраузерность. Достаточно того, что этот механизм будет работать на браузере клиента, каковым (о чудо!) является FF.

Читайте также:  Php return array include

Тут же вспоминаем, что в FF последних версий есть возможность получить в строку содержимое файла, загруженного в поле file-upload. И в голову приходит желание разбивать эту строку на куски и закачивать частями, используя Ajax.

Клиентская часть

Сначала нарисуем необходимое в статическом HTML:

Т.е. при нажатии на ссылку должна быть вызвана функция big_file_upload, в которую передается объект, из которого нужно взять содержимое файла. Обратите внимание на конструкуию $(‘#myfile’). Думаю, нет нужды подробнее останавливаться на необходимости подключения библиотеки jQuery, которую мы также будем использовать и для ajax-запросов при передачи файла на сервер.

Теперь нам надо написать ту самую функцию big_file_upload:

var upload_chunk_size = 120000; // В этой переменной лежит размер кусков, на которые мы будем делить файл. function big_file_upload(file) < // . >
Получение содержимого файла

Для получения содержимого файла будем использовать следующую конструкцию:

var data = file.get(0).files.item(0).getAsDataURL();
  • file.get(0) — получение DOM-объекта из jQuery-объекта, переданного в функцию
  • files.item(0) — получение первого файла из списка. Здесь он у нас единственный, однако напомню, что уже есть возможность множественной закачки файлов из одного контрола.
  • getAsDataURL() — получение содержимого файла в формате Data:URL. Есть еще методы getAsText и getAsBinary, однако нам нужно передавать на сервер методом POST, поэтому желательно получить содержимое файла, закодированное в Base64.
var filename = file.get(0).files.item(0).fileName;

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

var comma = data.indexOf(','); if (comma>0) data = data.substring(comma+1);
Отправка файла кусками
var pos = 0; while (pos); pos += upload_chunk_size; >

Серверная часть

Теперь сделаем на сервере принимающий PHP-скрипт upload.php. В варианте для примера он также предельно прост:

$filename = $_POST['filename']; $f = fopen("/dir/to/save/$filename","a"); fputs($f,base64_decode($_POST['chunk'])); fclose($f);

Файл открывается с опцией «а», т.е. предлагается не перезаписывать существующий файл, а дополнять его. Таким способом мы из кусков соберем целый файл.

Думаю, всем понятно, что данный скрипт должен лежать в закрытой админской части сайта чтобы к нему не имели доступа разные личности со стороны. Кроме того, имя файла и сам файл должны проверяться на валидность. Иначе — это даже не уязвимость, а… я и слова-то такого не знаю…

Пробуем запустить

Попробовали? Получилось? Держу пари, что получилось совсем не то, что ожидалось. Файл вроде бы закачался. Вроде бы даже длина правильная. А вот содержимое — какая-то каша.

Почему так получилось? Ответ прост: POST-запросы отправляются в асинхронном режиме по нескольку кусков одновременно, поэтому никто не гарантирует, что на сервер они будут приходить именно в той последовательности, в которой были поданы команды на отправку. Мало того, что никто не гарантирует, а я прямо-таки утверждаю, что никогда они не придут в нужной последовательности. Всегда будет получаться каша.

Поэтому, как это ни грустно, придется асинхронность отключить. Перед оправкой файла выполним следующее:

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

var upload_chunk_size = 120000; // Размер куска function big_file_upload(file) < var data = file.get(0).files.item(0).getAsDataURL(); // Получаем содержимое файла var filename = file.get(0).files.item(0).fileName; // Получаем имя файла var comma = data.indexOf(','); if (comma>0) data = data.substring(comma+1); // Отрезаем заголовок Data::URL var pos = 0; $.ajaxSetup(); // Отключаем асинхронность while (pos); pos += upload_chunk_size; var p = Math.round(pos*100/data.length); // Вычисляем процент отправленного $('#progress').text(p+'%'); // Рисуем цифирь для спокойствия пользователя > >

Недостатки

  1. В данном примере предполагается, что метод getAsDataURL всегда возвращает данные, закодированные в base64. На самом деле я бы не стал биться об заклад, что так будет всегда. По-хорошему, заголовок надо не выкидывать, а передавать серверной части, которую, в свою очередь, научить обрабатывать данные, закодированные разными способами.
  2. Файл, отправленный два раза, запишется на сервере два раза. Причем дополнит сам себя. Чтобы избежать этого, видимо, нужно передавать кроме имени еще и какой-то уникальный идентификатор закачки. Но это, вообще говоря, вопрос способа формирования и передачи имени файла. Здесь универсального рецепта нет и быть не может.
  3. Клиентский скрипт исполняется долго (в зависимости от размера файла и толщины канала), и FF может даже поинтересоваться: вы, мол, уверены ли, что надо ждать окончания, или убить его чтоб не мучался?
  4. Кроссбраузерность. Получение содержимого файла, увы, работает только на FF. Проверено на 3.0, 3.5 и 3.6. На более ранних не проверялось за неимением таковых под рукой. Сами разработчики FF рекомендуют вместо это способа пользоваться FileAPI, однако оно появилось только в 3.6.
  5. Действительно большие файлы (сотни мегабайт, гигабайты) закачать таким способом не получится. Предел зависит от объема памяти, доступной браузеру.

Что сделать?

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

Источник

Загрузка файлов больших размеров в PHP

Загрузка файлов больших размеров в PHP

Те, кто уже пытался загружать файлы, вполне вероятно, обратили внимание на то, что загрузить файл через PHP даже среднего размера (15-20 МБ) не представляется возможным. А ведь это не такой уж и большой размер файла, и в этой статье я расскажу, как загружать файлы больших размеров в PHP.

Для того, чтобы загружать файлы больших размеров в PHP, надо исправить настройки интерпритатора PHP. Существуют 4 настройки, влияющие на максимальный допустимый размер загружаемого файла:

  • max_execution_time — максимальное время, которое может выполняться скрипт. Как правило, стоит 30-60 секунд.
  • max_input_time — максимальное время, в течение которого могут приниматься данные на сервер. Здесь также стоит порядка 30-60 секунд.
  • upload_max_filesize — максимальный размер файла, который допускается для загрузки на сервер. Ключевая настройка, однако, вопреки общему заблуждению, далеко не единственная необходимая для загрузки больших файлов в РHP.
  • post_max_size — максимальный размер отправляемых данных.

Фактически, чтобы загружать большие файлы в PHP, нужно исправить все 4 настройки. Делается это с помощью файла .htaccess, в который нужно добавить следующие строки (безусловно, значения можно менять на свои):

php_value max_execution_time 500
php_value max_input_time 500
php_value upload_max_filesize 30M
php_value post_max_size 30M

Почему все 4 опции так важны:

  • max_execution_time — здесь нужно поставить время, за которое должен успеть загрузиться файл. Очевидно, что файл размеров 30 МБ вряд ли загрузится за 30-60 секунд, поэтому безусловно, это время надо увеличивать.
  • max_input_time — аналогично с max_execution_time. Ведь если первые 60 секунд будут приниматься данные, а потом перестанут, то вновь файл не успеет загрузиться.
  • upload_max_filesize — эту опцию меняют все и без проблем, однако, когда ничего не меняется — удивляются, а ведь она хоть и является ключевой, но, тем не менее, другие три так же очень важны.
  • post_max_size — максимальный размер отправляемых данных так же очевиден, ведь если файл большого размера, то он не сможет полностью отправиться из-за этого ограничения.

Создано 19.03.2011 14:25:11

  • Михаил Русаков
  • Копирование материалов разрешается только с указанием автора (Михаил Русаков) и индексируемой прямой ссылкой на сайт (http://myrusakov.ru)!

    Добавляйтесь ко мне в друзья ВКонтакте: http://vk.com/myrusakov.
    Если Вы хотите дать оценку мне и моей работе, то напишите её в моей группе: http://vk.com/rusakovmy.

    Если Вы не хотите пропустить новые материалы на сайте,
    то Вы можете подписаться на обновления: Подписаться на обновления

    Если у Вас остались какие-либо вопросы, либо у Вас есть желание высказаться по поводу этой статьи, то Вы можете оставить свой комментарий внизу страницы.

    Порекомендуйте эту статью друзьям:

    Если Вам понравился сайт, то разместите ссылку на него (у себя на сайте, на форуме, в контакте):

    1. Кнопка:
      Она выглядит вот так:
    2. Текстовая ссылка:
      Она выглядит вот так: Как создать свой сайт
    3. BB-код ссылки для форумов (например, можете поставить её в подписи):

    Комментарии ( 9 ):

    У меня сайт на CMS Joomla. Добавил изменения в файл .htaccess в корне сайта, но желаемого результата не получил. Возмоджно, есть еще какие-то настройки или я неправильно записал php_value в .htaccess. Подскажите, плиз, какая может быть причина? Спасибо

    Причина может быть в самой Joomla. В настройках Joomla есть параметр «Максимальный размер загружаемых файлов». Измените его. Также проблема может быть в Вашем хостинге, который не позволяет менять параметры через .htaccess.

    Спасибо, Михаил. Буду пробовать.

    Михаил, а вы можете написать статью про обрезку изображений?

    Михаил, здравствуйте! Создавал хостинг изображений по бонусу из вашего курса PHP и MySQL с Нуля до Гуру, но возникла проблема: на странице вывода изображений выводится такая ошибка — «Fatal error: Call to private method Image::loadImage() from context » in V:\home\localhost\www\myproject\show_image.php on line 3″. 3 ряд совпадет с Вашим в обучающем видео. Как решить эту проблему?

    Написал скрипт, который запускается кроном. Скрипт отправляет мне на почту бэкапы всех имеющихся на сервере баз данных. Проблема в том, что функция mail() не может отправить письмо с файлами, отдаёт false. На почту приходит уведомление: Mail failure — message too big A message that you sent was longer than the maximum size allowed on this system. It was not delivered to any recipients. Размер аттача в отправляемом письме не более 30 мегабайт В настройках php поставил максимальный размер файлов 300мб, не помогло. Может для отправляемых c сервера писем есть какие-то настройки?

    Данный вопрос вы можете задать своему хостеру. Но в целом да,скорее всего так и есть.Обычно размер аттача одного файла не может превышать 30-50 мегабайт

    Есть еще один удобный сервис который позволяет отправлять большие файлы. С его помощью можно отправлять больших файлы по электронной почте. Попробуйте www.jumbomail.me/ru/ вы сможете отправлять по электронной почте большие файлы до 5 ГБ бесплатно. Достаточно ввести адрес электронной почты получателя и загрузить их. Тут еще есть крутая фишка что можно просматривать фотки и слушать музыку не скачивая к себе в компьютер. Сама пользуюсь, рекомендую.

    Для добавления комментариев надо войти в систему.
    Если Вы ещё не зарегистрированы на сайте, то сначала зарегистрируйтесь.

    Copyright © 2010-2023 Русаков Михаил Юрьевич. Все права защищены.

    Источник

    Оцените статью