Как стать автором
Обновить

Комментарии 23

Было бы неплохо рассмотреть ещё сжатие данных (мы же zip'уем, а не tar'им) и защиту паролем.


Ну и объединение массивов стоило сделать через array_merge, а не сложением. (не заметил хаб Ненормальное программирование)

На всякий случай не буду обещать что-нибудь, но в планах маленький цикл статей о zip, php и nginx, что с этим всем можно делать. И тут zip больше как контейнер (не рассматривал tar, потому что он не так широко распространен среди обычных пользователей, хотя когда-то, давным-давно, по мотивам одной статьи писал tar-упаковщик, тоже на php). Но если будут силы, я очень постараюсь не обойти тему сжатия стороной.

Нисколько. Я посчитал, что это про написание уже написанного

А что там рассматривать zip по сути просто контейнер. Сами данные можно сжимать разными алгоритмами, deflate (тот же gzip без дополнительных gzip заголовков) или bzip2, ну и более продвинутые lzma (но это уже не все смогут распаковать).

Значительно интересней то, что zip позволяет сохранять потоки. Т.е. не обязательно точно знать размер и CRC файла для заголовка. Например, когда делаешь бэкап БД (на пару гигов) то не нужно сначала это всё во временный файл писать, а потом уже в zip. Можно сразу писать в zip, на лету считая CRC и размеры, и эти данные сохранять после файла (там специальные опции для этого) плюс в центральном каталоге. Вот это уже ни одна из многочисленных доступных реализаций на PHP (по сути они только необходимый минимум умеют) не поддерживает.
А можно даже этот бекап прям сразу на лету и заливать куда-нибудь в облако, не сохраняя у себя вообще никаких временных данных.
PHP позволяет считать хеши инрементально, так что это очень даже возможно и, вроде, не сложно.
Я думаю, что следующая статья как раз про это и будет, постараюсь только набросать хоть как-нибудь интересный proof of concept, чтоб это можно было запустить и пощупать результат.
Чисто технически нет проблем, я даже прикручивал сжатие и шифрование на лету. Можно покопать в сторону фильтров (именно на них делал), ну и будет не совсем банальное решение. Но в реальности вылазит такое ограничение, как скорость аплоада. Т.е. аплоад на облако может длиться в десятки раз дольше самого бэкапа. Если заливать на тот же Dropbox. То лучше всё же сохранять бэкап на локалке, чтобы сделать это как можно быстрее и разлочить базу. А потом уже потихоньку заливать. Причем заливать можно в несколько параллельных потоков, что сильно ускоряет процесс.

Ну и в любом случае, решение проверить на больших файлах (чтобы не помещались в разнообразные буферы), в основном все «вкусности» там вылазят.

Спасибо большое за статью!


Всегда нравились с потрошением каких-нибудь популярных штук, общаться с которыми (со штуками) доводилось только через API.

С моей точки зрения, наиболее жизненное использование знаний приведённых в статье — это организация скачивания из веб-приложения нескольких файлов одним запросом. Подобное реализовано в популярных облачных хранилищах файлов.
Причём реализовать это можно в виде «realtime стриминга» без предварительного создания zip-файла на диске сервера. И ещё будет возможность докачки после разрыва связи, т.к. за счёт отсутствия сжатия мы можем быстро рассчитать итоговый размер архива, и определять что должно находится по любому смещению от его начала.
У популярных облачных хранилищ скорей всего есть предрасчитанный crc32, что немного им упрощает дело.
Я постараюсь в следующей статье показать вариант, когда мы даже не знаем размер упаковываемых файлов (например, у нас список URL, а мы хотим собрать zip-архив с содержимым), но можем это все упаковывать на лету и отдавать пользователю одновременно с прогрессом скачивания файла нами.
Но и про ваш вариант, думаю, расскажу :)
Приходилось делать подобную систему. Там файлы для «упаковки» собирались из достаточно больших блоков (до 3Гб макс, файл чуть меньше 4Гб макс — ограничение зипа), и часть блоков была динамическая. Пересчитывать crc для всего файла на лету при таких размерах, понятно, не вариант. :)
Рассчитанные crc всех статических блоков хранились, для динамических считались в процессе генерации и подгонялись так, чтобы общий crc файла сходился.
Отдавалось всё это через nginx с использованием SSI, в итоге пхп на выходе выдавал динамические блоки + SSI-разметку для nginx, а тот уже сам «собирал» итоговый файл из разных блоков с диска по мере необходимости.
Точно так же потом собирался и рар-архив без сжатия.
Ещё таймстэмпы, конечно, реальные были, и куча всего другого :)

И докачка была, с вычислением смещения и отдачей только нужного куска.

Зип со сжатием тоже делал, но для файлов поменьше. Теоретически, его тоже можно делать с предрассчитанными «блоками» (при аналогичной задаче «сборки» файла из блоков), надо поковыряться немного с кишками deflate для уточнения, но большие всё равно были уже жатые так что необходимости в этом так и не возникло.

Всё собирался статью писать, но не думаю, что дойдут руки… Так что если вдруг интересны какие-то детали…
Большое спасибо!
Очень люблю почитать вот такие истории, особенно когда в них фигурирует php:)

Насчет ssi думал когда нужно было склеивать несколько файлов и отдавать как один, но как-то дальше думать это не пошло и пока все это дело на lua.
Вообще реально им собирать несколько удаленных файлов в один?
То есть у меня есть location с proxy_pass, который отдает файл откуда-то.
но файл может быть разбит на насколько (ну, например, зип-архив просто разбитый на куски по 200мб, не многотомник) и пользователю нужно, соответственно, отдать нормальный файл, а не 2-3куска. я могу отдать из php такой ssi, чтоб nginx последовательно подсунул пользователю несколько кусков как один файл?

Ну да, конечно. Там получается в итоге просто последовательно несколько SSI-тегов со ссылками на соответствующие куски/локейшны. И между ними если надо можно вставлять всё, что душе угодно — nginx корректно всё обрабатывает. На другом проекте по тому же принципу менялась иконка в exe-файле — подготовленные блоки + рассчитанные куски секции ресурсов, заголовки, контрольные суммы, и т.п. PHP отдавал nginx-у мешанину из байтиков и ssi-тегов, а тот уже всё собирал по мере отдачи файла.
Т.е. например если юзер прерывал загрузку в самом начале nginx уже не читал следующие блоки с диска.
Спасибо, надо будет попробовать прикрутить ssi, пощупать.
С lua не очень нравится ресурсоемкость, — по ощущениям раза в два больше, чем голые proxy_pass жрёт, поэтому хотелось бы использовать её только как header_filter и т.п.
Конечно, любой адекватный человек скажет, что писать архиваторы на php это бесполезная затея
Один знакомый писал рекурсивный упаковщик на Basic. Да, рекурсивный в смысле ненулевого прироста сжатия в каждой итерации. На вид был вполне нормальным. Чем закончился проект — засекречиванием или белой рубашкой с не по росту длинными рукавами — не знаю, потерялись.
На самом деле я, конечно же, утрирую — мы же не на голом PHP хеш считаем, а больше там числодробительного ничего и нет. Практически только IO-операции.
Возможно, с появлением jit в релизных версиях, подобные темы вообще станут чем-то обыденным для нашего сообщества.
Не забывайте про фактор кодировки для имени файла в архиве!

По поводу применения — таким подходом я пользовался, чтобы дать пользователям возможность скачивать несколько файлов за раз…
Весьма интересует распаковка файлов с переставленными байтами. А конкретно файлов ресурсов .idf
Вроде и алгоритм запаковки есть, а ума написать распаковщик нету.
Ну, я с zip тоже не за 5 минут разобрался)
Главное время и упорство, а там все получится.

Попробуйте http://kaitai.io/ — очень полезная штука для ковыряния в кишках бинарников.

Мерси! Попробую.
Делал подобную дичь для использования в 1с, которая хранит сжатые данные в deflate: к ним пришлось прикручивать все рассчитанные секции zip-формата, чтобы потом можно было извлечь данные из полноценного zip-архива.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.