Создание ZIP-файлов с помощью PHP + Apache на лету на высокой скорости? - PullRequest
9 голосов
/ 13 июня 2009

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

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

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


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

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

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

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

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

Проблемы, которые я выявил, таковы:

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

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


Добавлено:

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

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

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

Ответы [ 5 ]

9 голосов
/ 13 июня 2009

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

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

1 голос
/ 17 ноября 2009

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

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

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

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

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

Я надеюсь, что могу помочь кому-то с этим:)

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

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

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

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

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

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

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

1 голос
/ 13 июня 2009

Вам нужно будет сохранить сгенерированный 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, если хотите, и просто найти нужное место.

Теперь к вашим вопросам

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

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

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

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

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

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

1 голос
/ 13 июня 2009

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

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

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

0 голосов
/ 12 декабря 2011

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

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

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