LINUX.ORG.RU

Zip 2 Bzip2

 ,


1

1

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

echo "asdfafsdasdf" | php myscript.php | bzip2 ... > file.zip.bz2

В этом конвейере «php myscript.php» как раз и возвращает zip-архив, выполняя ещё операции объединения STDIN с нужными мне данными.

И всё бы хорошо, но в результирующем архиве file.bz2 лежит один файл архива zip, а не его непосредственное содержимое (а мне хотелось бы именно так) как в случае использования tar. То есть получили архив в архиве. Можно ли как-то в результирующем архиве получить именно содержимое zip-архива, а не сам zip-файл?

В случае с таром происходит тоже самое. bzip может сжимать только один файл. Просто тар поддерживает прозрачную распаковку своих архивов.

alchemist ()
Последнее исправление: alchemist (всего исправлений: 1)
Ответ на: комментарий от alchemist
tar cf - plummet.png test.php | bzip2 -6 -c - > ../archive.tar.bz2

В результате в bz2-архиве будет два архивируемых файла, а не один Tar-архив. Мне не так уж принципиально использовать компрессор bzip2, можно и xz, например. Или какую-то промежуточную дополнительную операцию, которая приведёт zip к tar или что-то в этом роде.

Novascriptum ()
Ответ на: комментарий от Novascriptum

В результате в bz2-архиве будет два архивируемых файла, а не один Tar-архив.

Нет, там один TAR. gzip, bzip2, xz, compress, …, тысячи их — всегда сжимают один файл. При этом неправильно говорить, что этот файл в каком-то архиве. Tar — архиватор, bzip2 — компрессор.

anonymous ()

Если ты просто хочешь сжать данные, то тебе не нужен архиватор, просто выводи свои данные в stdout и через пайп потом в компрессор:

$ echo data | xz > compressed.xz
$ xzcat compressed.xz
data
$ xzcat compressed.xz > decompressed.txt

или даже

$ echo data | lrzip -qzL9 > compressed.lrz
$ lrzcat -q compressed.lrz | tee
data
$ lrzcat -q compressed.lrz > decompressed.txt

Или я тебя не понял.

anonymous ()
Ответ на: комментарий от Novascriptum

Из всего упомянутого, так, как ты хочешь - работает только zip

alchemist ()
Ответ на: комментарий от alchemist

А можно ли как-то в этом конвейере преобразовать zip к tar'у и уже его потом сжимать?

Novascriptum ()
Ответ на: комментарий от anonymous

Я бы с радостью, но мне нужно объединить эти данные с другими в потоковом режиме, для этого и вставлен промежуточный php-скрипт (он читает свой STDIN и формирует на его основе STDOUT, объединяя с другими файлами). И он выдаёт zip-архив, tar сформировать не могу. Отсюда и проблема.

Novascriptum ()
Ответ на: комментарий от anonymous

Нет, там один TAR. gzip, bzip2, xz, compress, …, тысячи их — всегда сжимают один файл. При этом неправильно говорить, что этот файл в каком-то архиве. Tar — архиватор, bzip2 — компрессор.

Так ведь если выполнить ту команду, которую я описал

tar cf - plummet.png test.php | bzip2 -6 -c - > ../archive.tar.bz2
, то в содержимом bz2-архива будут сразу эти два файла (png и php), а не один tar-архив (я проверял). Я понимаю, что архиватор и компрессор это разное. Но меня интересует конечный результат - сжатый архив. И чтобы в нём было несколько файлов. У меня есть zip. И мне с ним надо что-то сделать, чтобы получить bz2, xz или что-то ещё с более хорошим сжатием, чем zip. И очень бы хотелось не распаковывать его потом два раза (один раз наружный bz2, потом внутренний zip), чтобы получить файлы.

Novascriptum ()
Ответ на: комментарий от Novascriptum

то в содержимом bz2-архива будут сразу эти два файла (png и php), а не один tar-архив (я проверял).

Не будут, ты неправильно проверял. Проверял командой tar поди?

anonymous ()
Ответ на: комментарий от Novascriptum

то в содержимом bz2-архива будут сразу эти два файла (png и php)

Нет, не будут. И вообще не бывает bz2-архивов. bz2 не архиватор, а компрессор. Он просто жмёт один файл и всё. Архиватор тут tar. bz2 жмёт один файл tar. Он является tar-архивом. И в нём сразу эти два файла.

И bz2 и xz жмут один файл. Если тебе нужен архив, для этого существует tar. Используй tar.xz (xz жмёт сильнее и быстрее, чем bz2, кстати).

Если очень хочется комбайн, как .zip — и архиватор и компрессор два в одном, но с большей степенью сжатия, есть 7z.

Psych218 ★★★★★ ()

получилось только zip. Но его степень сжатия меня не устраивает
нужно объединить эти данные с другими в потоковом режиме

звучит прохладно: поток плохо сжимается

anonymous ()
Ответ на: комментарий от Novascriptum

объединяя с другими файлами

То есть скриптом, на основе данных из stdin ты изменяешь файлы, которые потом архивируешь, так?

anonymous ()
Ответ на: комментарий от anonymous

То есть скриптом, на основе данных из stdin ты изменяешь файлы, которые потом архивируешь, так?

PHP берёт данные из stdin и кладёт в sql-файл, который в потоковом режиме передаёт в zip-архив в stdout, не создавая на диске. Когда передал, он не закрывает stdout и продолжает в тот же zip передавать другие файлы, которые читает с диска. Я бы рад таким образом tar создавать вместо zip, это бы избавило от кучи проблем. Но не могу найти php-библиотеку, которая это делает, чтобы могла подхватывать stdin - есть только для чтения с диска (а отличие в том, что при чтении с диска мы заранее знаем размер файлов и можем записать в мета-информацию tar-архива, а при чтении из stdin - нет). Нашёл только для zip.

Novascriptum ()

Tar использовать не получилось по ряду непреодолимых препятствий

Давай лучше оригинальной проблемой займемся. Что там за препятствия такие непреодолимые? Обязательно ли в этой связки использовать php?

slowpony ★★ ()
Ответ на: комментарий от slowpony

Давай лучше оригинальной проблемой займемся. Что там за препятствия такие непреодолимые? Обязательно ли в этой связки использовать php?

Разумно.
В общем случае задача в том, чтобы:
1) Сделать дамп базы mySQL
2) Сделать бэкап файлов
3) Объединить их в один архив
4) Сжать его
5) Если размер сжатых данных меньше 2 Мб, то запихнуть в архив утяжеляющий груз (картинку)
6) Если размер сжатых данных большой, то распилить его на крупные части (по 9-10 Гб)
7) Отправить сжатые части на FTP
8) и самое интересное: всё сделать без создания файлов на диске, т.е. общим конвейером.

Задача 100% решаема. Проблемы, с которыми столкнулся:
1) утилита tar не понимает stdin, соответственно, не может принять дамп базы
2) утилита tar не поддерживает дописывание в уже сформированный архив (насколько мне удалось выяснить), поэтому в архив сложно будет добавить картинку
3) утилита split не поддерживает передачу частей в stdout
4) в конвейере сложно определить размер передаваемых данных, поэтому сложно без собственных скриптов положить картинку в архив, если он меньше 2 Мб (надо же узнать, что он меньше).

Тем не менее, все эти проблемы решаются написанием собственных скриптов. Так как лучше всего я знаю PHP, то его и выбрал. Единственная трудность, с которой столкнулся - это создание tar-архива на основе stdin-данных. Есть библиотеки, которые в этом здорово могут помочь:
ArchiveStream - умеет создавать tar и дописывать в него, но не умеет формировать его на основе STDIN
ZipStream - fork от предыдущей библиотеки, который позволяет создавать архив из STDIN, но не поддерживает tar (только zip).
Пытался поковыряться в коде ArchiveStream, чтобы создавать tar из stdin, но столкнулся с тем, что при добавлении файлов в архив нужно указывать их размер - http://i.imgur.com/IudxI4G.png (скрин с гитхаба). А его нет, это ж поток. Если указать ноль, то архив битый получается.
Если повторно выполнить метод, добавляющий эту мета-информацию, когда файл уже будет передан до конца и станет понятно, каков его размер - то тоже херня какая-то получается, архив в результате тоже битый.
Скрестить бы как-то возможности ArchiveStream и ZipStream в плане работы с tar-архивами, было бы мне вселенское счастье. Но что-то своих мозгов не хватает, плохо понимаю бинарную структуру таров, чтобы правильные мета-данные передать.
В это и упёрся.

Novascriptum ()
Ответ на: комментарий от Novascriptum

ArchiveStream - умеет создавать tar и дописывать в него, но не умеет формировать его на основе STDIN

А как же

// Create a file named 'hello.txt'
$zip->add_file('hello.txt', 'This is the contents of hello.txt');

goingUp ★★★★★ ()
Ответ на: комментарий от goingUp

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

Novascriptum ()
Ответ на: комментарий от goingUp

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

Novascriptum ()
Ответ на: комментарий от Novascriptum

таровская мета-информация записывается в начало файла

Похоже, что тут я не прав, пойду ещё мануалы почитаю.

Novascriptum ()
Ответ на: комментарий от Novascriptum

Не, вроде всё верно. Перед тем как передавать контент, нужно установить header-блок, в который записать размер контента. А он заранее не известен, то tar использовать вообще не получится. Получается, так?

Novascriptum ()
Ответ на: комментарий от Novascriptum

В крайнем случае можно реализовать свой «велосипед». Следует просто соединить нужные файлы один за другим в большой файл, а в начале файла указать информацию о названиях файлов и соответствующие им диапазоны байт, если необходимо - структуру каталога, атрибуты файлов. В самом простом варианте этот начальный блок, содержащий информацию о файлах, должен иметь строго определенный размер, все лишнее место в файле забивается, скажем, нулями. Затем этот файл следует сжать произвольным компрессором. Когда потребуются файлы - повторить эти операции в обратном порядке. Описанное выше реализовать очень просто. Но это совершенно ненужно, потому что я уверен, что вы делаете что-то не так. Например,самый простой вариант - создать в оперативной памяти виртуальный раздел и работать с файлами в нем с помощью стандартных tar, bz2 и.т.д. Если файл слишком большой - дробить его на части, прикрепляя к нему информацию, которую затем использовать для восстановления его целостности.

anonymous ()
Ответ на: комментарий от Novascriptum

PHP берёт данные из stdin и кладёт в sql-файл

ok, файл на диске? А кладёт как, через DBMS и update/insert или в бинарном режиме в потоке?

который в потоковом режиме передаёт в zip-архив в stdout

зачем? Почему нельзя добавить в архив собственно изменённый sql-файл?

В каменте ниже ты пишешь:

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

Я правильно понимаю, что ты изменяешь sql-файл на диске, а потом побайтово читаешь изменённый файл и отправляешь в zip? Можно ли сразу изменить все файлы на диске, чтобы потом их заархивировать и сжать?

--

Если что, то у tar довольно простой формат.

Я сейчас накидал скрипт на питоне - всё работает. И даже больше: так как xz поддерживает конкатенацию, то вполне себе можно добавлять файлы в сжатый архив:

$ ./test.py test.bin | xz > test.tar.xz
$ tar tf test.tar.xz
test.bin
$ ./test.py thumb.jpg | xz >> test.tar.xz
$ tar tf test.tar.xz
test.bin
thumb.jpg
anonymous ()
Ответ на: комментарий от anonymous

ok, файл на диске? А кладёт как, через DBMS и update/insert или в бинарном режиме в потоке?

В бинарном режиме в потоке. Я, может, непонятно мысль свою изложил, php ничего не меняет в sql. Он просто делает mysqldump и передаёт его результат в архив в виде отдельного файла (не создавая его на диске). Он может сделать несколько mysqldump'ов и положить их в один и тот же архив в разные файлы. И потом в этот же архив положить другие файлы, читая их с диска. Сам файл архива на диске не создаётся, но передаётся в поток прямо на FTP-сервер.

Я сейчас накидал скрипт на питоне - всё работает

В Питоне не силён, завтра проконсультируюсь с коллегой, который его знает, по поводу вашего скрипта. Но насчёт вашего примера: тут же скрипт файлы с диска читает, в этом нет сложности, потому что у этих файлов можно прочитать размер и сформировать корректный header-блок файла для tar-архива. Сложность возникает тогда, когда файлы, которые нужно засунуть в архив, берутся из потока stdin. Тогда мы не знаем их точный размер и не можем сформировать header-блок.

так как xz поддерживает конкатенацию, то вполне себе можно добавлять файлы в сжатый архив

А вот за это спасибо, поизучаю эту тему.

Novascriptum ()
Ответ на: комментарий от Novascriptum

Тогда мы не знаем их точный размер и не можем сформировать header-блок

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

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.