Создание ZIP-файлов с PHP + Apache на лету на высокой скорости?

Процитировать некоторые известные слова :

«Программисты … часто укрываются в понятной, но катастрофической, склонности к сложности и изобретательности в своей работе. Запрещено создавать что-либо большее, чем программа, они отвечают, делая эту программу достаточно сложной, чтобы бросить вызов их профессиональному мастерству ».

Решая какую-то обыденную проблему на работе, я придумал эту идею, и я не совсем уверен, как ее решить. Я знаю, что не буду реализовывать это, но мне очень любопытно, какое лучшее решение. 🙂


Предположим, у вас есть эта большая коллекция с JPG-файлами и несколькими нечетными SWF-файлами. С «большой» я имею в виду «пару тысяч». Каждый файл JPG составляет около 200 КБ, а размер SWF может составлять до нескольких МБ. Каждый день есть несколько новых файлов JPG. Общий размер всего материала составляет около 1 ГБ и медленно, но неуклонно растет. Файлы очень редко меняются или удаляются.

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

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

Поскольку количество критериев достаточно велико, я не могу предварительно генерировать все возможные ZIP-файлы и должен делать это на лету. Другая проблема заключается в том, что загрузка может быть довольно большой, а для пользователей с медленными соединениями вполне вероятно, что это займет час или больше. Поэтому поддержка «возобновления» является обязательной.

На яркой стороне, однако, ZIP не нужно ничего сжимать – файлы в основном являются файлами JPEG. Таким образом, весь процесс не должен быть более интенсивным для процессора, чем простая загрузка файла.

Таким образом, проблемы, которые я определил, таковы:

  • PHP имеет таймаут выполнения для скриптов. Хотя он может быть изменен самим сценарием, не будет никаких проблем, удалив его полностью?
  • С помощью опции resume есть возможность изменения результатов фильтрации для разных HTTP-запросов. Это может быть смягчено путем сортировки результатов в хронологическом порядке, поскольку сбор только увеличивается. Затем URL-адрес запроса включал бы дату, когда он был первоначально создан, и сценарий не рассматривал файлы младше этого. Будет ли этого достаточно?
  • Будет ли передача большого количества файлов данных через PHP не будет хитом производительности сама по себе?

Как бы вы это реализовали? Является ли PHP до этой задачи вообще?


Добавлено:

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

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

В результате файлы ZIP будут довольно медленно генерировать (из-за большого объема данных и скорости диска) и будут содержать всю коллекцию много раз. Я не вижу, как это решение будет работать без какого-нибудь мега-дорогостоящего массива SCSI RAID.

Это может быть то, что вам нужно: http://pablotron.org/software/zipstream-php/

Эта библиотека позволяет вам создавать динамический потоковый zip-файл без замены на диск.

Используйте, например, библиотеку Zip библиотеки PhpConcept .

Возобновление должно поддерживаться вашим веб-сервером, за исключением случаев, когда вы не делаете zip-файлы доступными напрямую. Если у вас есть php-скрипт в качестве медиатора, обратите внимание на отправку правильных заголовков для поддержки возобновления.

Сценарий, создающий файлы, не должен тайм-аут никогда не заставлять пользователей сразу выбирать тысячи файлов. И держите что-то на месте, чтобы удалить «старые zip-файлы» и следите, чтобы какой-то злонамеренный пользователь не использовал ваше дисковое пространство, запрашивая много разных коллекций файлов.

Вам придется хранить сгенерированный zip-файл, если вы хотите, чтобы они могли возобновить загрузку.

В основном вы создаете zip-файл и вставляете его в каталог / tmp с повторяемым именем файла (может быть, хеш фильтров поиска). Затем вы отправляете правильные заголовки пользователю и echo file_get_contents пользователю.

Чтобы поддержать возобновление, вам нужно проверить значение $ _SERVER ['HTTP_RANGE'], этот формат подробно описан здесь, и как только вы проанализировали, что вам нужно запустить что-то вроде этого.

$size = filesize($zip_file); if(isset($_SERVER['HTTP_RANGE'])) { //parse http_range $range = explode( '-', $seek_range); $new_length = $range[1] - $range[0] header("HTTP/1.1 206 Partial Content"); header("Content-Length: $new_length"); header("Content-Range: bytes {$range[0]}-$range[1]"); echo file_get_contents($zip_file, FILE_BINARY, null, $range[0], $new_length); } else { header("Content-Range: bytes 0-$size"); header("Content-Length: ".$size); echo file_get_contents($zip_file); } 

Это очень отрывочный код, вам, вероятно, придется немного поиграть с заголовками и содержимым в переменной HTTP_RANGE. Вы можете использовать fopen и fwrite, а не содержимое file_get, если хотите, и просто fseek в нужном месте.

Теперь на ваши вопросы

  • PHP имеет таймаут выполнения для скриптов. Хотя он может быть изменен самим сценарием, не будет никаких проблем, удалив его полностью?

Вы можете удалить его, если хотите, однако, если что-то пойдет грушевидно, и ваш код застрянет в бесконечном цикле, это может привести к интересным проблемам, если бесконечный цикл будет регистрироваться и где-то ошибка, и вы не заметите, пока не появится довольно грубая sys-admin задается вопросом, почему на их сервере закончилось свободное место на жестком диске;)

  • С помощью опции resume есть возможность изменения результатов фильтрации для разных HTTP-запросов. Это может быть смягчено путем сортировки результатов в хронологическом порядке, поскольку сбор только увеличивается. Затем URL-адрес запроса включал бы дату, когда он был первоначально создан, и сценарий не рассматривал файлы младше этого. Будет ли этого достаточно?

Загрузите файл на жесткий диск, значит, у вас не будет этой проблемы.

  • Будет ли передача большого количества файлов данных через PHP не будет хитом производительности сама по себе?

Да, это будет не так быстро, как обычная загрузка с веб-сервера. Но это не должно быть слишком медленным.

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

и у меня были подобные идеи, как вы. подход к сдаче компрессии очень хорош, так как вам даже не нужно меньше ресурсов процессора, вы сохраняете память, потому что вам не нужно прикасаться к входным файлам и передавать их через tt, вы также можете рассчитать все, как заголовки zip и end filesize очень легко, и вы можете перейти к каждой позиции и генерировать с этого момента, чтобы реализовать резюме.

Я иду еще дальше, я генерирую одну контрольную сумму из всех входных файлов crc и использую ее как e-tag для сгенерированного файла для поддержки кеширования и как часть имени файла. Если вы уже загрузили созданный zip-файл, браузер получает его из локального кеша вместо сервера. Вы также можете настроить скорость загрузки (например, 300 КБ / с). Можно сделать zip-комментарии. Вы можете выбрать, какие файлы можно добавить, а что нет (например, thumbs.db).

Но theres одна проблема, которую вы не можете полностью преодолеть с помощью формата zip. Это генерация значений crc. Даже если вы используете хеш-файл для преодоления проблемы с памятью или используете хеш-обновление для постепенного создания crc, он будет использовать много ресурсов процессора. Не много для одного человека, но не рекомендуется для профессионального использования. Я решил это с дополнительной таблицей значений crc, которую я генерирую с помощью дополнительного скрипта. Я добавляю эти значения crc для каждого параметра в класс zip. При этом класс очень быстрый. Как и обычный сценарий загрузки, как вы упомянули.

Мой zip-класс работает, вы можете посмотреть его здесь: http://www.ranma.tv/zip-class.txt

Надеюсь, я смогу помочь кому-то в этом 🙂

Но я прекращу этот подход, я перепрограммирую свой класс классу tar. С tar мне не нужно генерировать значения crc из файлов, для tar нужны только контрольные суммы для заголовков, вот и все. И мне больше не нужна дополнительная таблица mysql. Я думаю, что это упрощает использование класса, если вам не нужно создавать дополнительную таблицу crc для него. Это не так сложно, потому что структура файлов с осями проще, чем структура zip.

PHP имеет таймаут выполнения для скриптов. Хотя он может быть изменен самим сценарием, не будет никаких проблем, удалив его полностью?

Если ваш сценарий безопасен и закрывается при прерывании пользователя, вы можете полностью удалить его. Но было бы безопаснее, если бы вы просто обновили таймаут на каждом файле, который вы проходите через 🙂

С помощью опции resume есть возможность изменения результатов фильтрации для разных HTTP-запросов. Это может быть смягчено путем сортировки результатов в хронологическом порядке, поскольку сбор только увеличивается. Затем URL-адрес запроса включал бы дату, когда он был первоначально создан, и сценарий не рассматривал файлы младше этого. Будет ли этого достаточно?

Да, это сработает. Я создал контрольную сумму из входного файла crc. Я использовал это как e-tag и как часть имени файла zip. Если что-то изменилось, пользователь не сможет возобновить созданный почтовый индекс, потому что электронный тег и имя файла изменились вместе с контентом.

Будет ли передача большого количества файлов данных через PHP не будет хитом производительности сама по себе?

Нет, если вы только проходите, он не будет использовать гораздо больше, чем обычную загрузку. Может быть, 0,01% я не знаю, его немного 🙂 Я предполагаю, что php не очень много делает с данными 🙂

Вы можете использовать ZipStream или PHPZip , которые будут отправлять zip-файлы «на лету» в браузер, разделенные кусками, вместо того, чтобы загружать весь контент на PHP, а затем отправлять zip-файл.

Обе библиотеки – полезные и полезные фрагменты кода. Несколько деталей:

  • ZipStream «работает» только с памятью, но при необходимости не может быть легко перенесен на PHP 4 (использует hash_file () )
  • PHPZip записывает временные файлы на диск (потребляет столько места на диске, как самый большой файл для добавления в zip-файл), но при необходимости может быть легко адаптирован для PHP 4.