Php pear archive tar

Работа с архивами tar и gz средствами PHP

Как это часто случается, все началось с того, что мне потребовалось нечто, позволяющее обрабатывать архивы «tar.gz» средствами php. Покопавшись в Интернете, я с удивлением обнаружил, что ничего сколько-нибудь приемлемого на эту тему не опубликовано.

1. PEAR расширение для PHP http://pear.php.net/package/Archive_Tar Прекрасно, но в моем случае – неприемлемо, поскольку доступа к настройкам сервера у меня нет. Вынужденно отметаем.
2. Отменная статья Алексея Валеева «Работа с архивами tar.gz в php». То, что нужно, но – увы. Мне требовалось решение лицензионно «прозрачное», не способное вызвать вопросов. По этому, использование библиотеки от Битрикса тоже не годилось.

Собственно, это – все.

Дальнейшее просеивание поисковиков ничего разумного не выдало. Немного подумав, я залез в код популярного net2ftp, который, как я помню, прекрасно архивы tar обрабатывает. Выяснилось, что есть на свете библиотека pcltar.lib.php от Vincent Blavet, 2001 года. Лицензия GNU. Все, как надо. Но! Для начала меня смутил размер самой библиотеки 127 килобайт. Ну, есть у меня бзик со старых времен — до сих пор считаю байты. Потом, мне хотелось иметь результат в виде класса, а не отдельными функциями. К тому же, взыграл азарт. Захотелось разобраться досконально.

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

Читайте также:  Python send to tcp socket

Итак, как известно, tar архиватором в современном понимании не является. Разработанный для сохранения данных на ленточных носителях, сжимать он не умеет, а просто объединяет множество файлов в один, добавляя свои заголовки, и дополняя получившийся код до ровного количества блоков по 512 байт. Затем результат уже можно сжимать архиватором. Каким? Да, хоть rar-ом. Без разницы. Хотя, традиционно для этого используются форматы gzip и bzip2. Поскольку они-то как раз два файла связать не могут (это диктуется принятой в unix-системах политикой «одна программа – одно действие»). Поддержка gzip и bzip2 в PHP обеспечивается сторонними библиотеками, и нам не важна. Важен сам tar.

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

100 байт name — название (может содержать относительный путь);
8 байт mode file mode
8 байт uid — user ID
8 байт gid — group ID
12 байт size — размер файла, байт (кодирована в восьмеричной системе)
12 байт mtime – дата и время последней модификации в секундах эпохи UNIX (кодирована в восьмеричной системе)
8 байт chksum – контрольная сумма заголовка (не файла!)
1 байт typeflag – определяет файл у нас, или каталог: файл – 0, каталог — 5
100 байт linkname – ссылка на файл
— дальше – поля «нового» формата — 6 байт magic – содержит слово «ustar», т.е. признак «нового» формата
2 байт version – версия нового формата (может отсутствовать)
32 байт uname – имя владельца
32 байт gname – имя группы владельца
8 байт devmajor – старший байт кода устройства
8 байт devminor – младший байт кода устройства
155 байт prefix – префикс (расширение) имени

Читайте также:  Массив переменных в классе php

Не используемые байты должны быть пустыми, хотя допускается код «20» (пробел).

Большая часть этих данных в общем случае не требуется. Лично меня интересовали имя, размер и дата.

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

В сущности, этих знаний достаточно, чтобы попробовать запаковать файл.

1. Откроем архив командой fopen(имя_файла).

2. Заголовок. Это самая сложная часть проблемы. Велосипед я изобретать не стал, воспользовавшись функцией из упомянутой библиотеки pcltar.lib.php, слегка её оптимизировав. Приводить здесь весь код не буду из-за объемности, но суть заключается в следующих действиях:
— Определяем имя файла, его размер, дату создания, выставленные на него права. Для каталогов размер указываем нулевым;
— Неиспользуемые параметры объявляем пустыми;
— Численные параметры (размер, дату) переводим в восьмеричную систему;
— Форматируем каждый параметр в соответствии с заявленными размерами соответствующих полей. Здесь есть одна хитрость, с которой я разобрался не сразу – на самом деле, значимая часть каждого поля должна быть на один байт меньше, чем размер самого поля. Последний же байт обязательно должен быть пустым. Иначе архив не читается.
— Пакуем все параметры в две отдельные строки. В две, поскольку между ними должна быть контрольная сумма заголовка.
— Считаем эту контрольную сумму, форматируем по тем же правилам, пакуем.
— А теперь пишем в файл архива последовательно три строки: первую часть параметров, контрольную сумму и вторую часть параметров.

Готово! Вот пример для времени создания:

$mtime = sprintf(«%11s «, DecOct(filemtime($filename)));

pack(«a100a8a8a8a12a12», …, …, …, …, …, $mtime);

3. С телом файла совсем все просто, Vincent Blavet в своей библиотеке его тоже обрабатывает функцией pack. Но я провел несколько экспериментов с различными файлами и не увидел искажений при паковке / распаковке. По этому, ради выигрыша производительности, делать не стал – нет смысла. Просто читаем данные из файла, разумеется – предварительно его открыв, и пишем в архив. Поскольку размеры файлов в моем случае могли оказаться достаточно большими, делаю я это блоками. Размер блока я принял за 50 Кб.

$infile = fopen($filename, rb);
$j = ceil(filesize($filename) / 51200) + 1;
for($i=0; $i $fr = fread($infile, 51200);
if ($this->tarmode == «tar»)
@fputs($this->tarfile, $fr);
else
@gzputs($this->tarfile, $fr);
>
fclose($infile);

4. А теперь добиваем до «ровного». Для этого нам нужно знать сколько байт «не хватило». Если файл меньше 512 байт, то это определяется вычитанием его размера из 512. Если же больше, определяем остаток от деления размера файла на 512, и вычитаем его из 512. Результат пакуем в бинарную строку.

Следует, также, учесть тот случай, когда файл изначально кратен 512 байтам – некоторые программы, самостоятельно дополняют свои файлы до нужного размера. Разумеется, в этом случае ничего не дописывать не требуется.

$ffs = filesize($filename);
if($ffs > 512)
$tolast = 512 — fmod($ffs, 512);
else
$tolast = 512-$ffs;
if($tolast != 512 && $tolast != 0) $fdata = pack(«a».$tolast, «»);
Полученные данные записываем в файл.
>

Результат – архив в формате «tar». Можно теперь повторить операцию со следующим файлом, или закрыть архив.
Если у нас подключена библиотека Zlib, то в процессе создания архив можно сжать, получив в итоге «tar.gz» или «tgz», кому что нравится. Проверить наличие библиотеки проще всего путем проверки константы FORCE_GZIP. Для автоматизации процесса, я ввел такую проверку для всех операций с архивным файлом. Примерно, так:

if(defined(‘FORCE_GZIP’))
$resopen = @fopen($this->tarname, ‘a+b’);
else
$resopen = @gzopen($this->tarname, ‘a+b’.$this->tarlevel);

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

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

Получить список файлов в архиве можно найдя и прочитав все заголовки. Для этого читаем первые 512 байт архива – это в любом случае будет заголовок и распаковываем его функцией unpack(). Поскольку unpack производит распаковку в ассоциативный массив, заодно присваиваем параметрам понятные наименования. Вот так:

unpack(«a100name/a8perms/…и так далее…», “считанные данные”)

Время создания и размер необходимо перевести обратно в десятичное счисление.

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

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

Единственные две сложности здесь, связаны с особенностями библиотеки Zlib:

Во-первых. Обнаружилось, что в функции gzopen данной библиотеки, не реализован модификант «+» для открытия файла одновременно на запись и на чтение, аналогично функции fopen. Пришлось отказаться от единого открытия / закрытия файла архива, и повторять эти операции при каждом обращении, в соответствии с задачей.

Во-вторых, в документации указано (и я убедился в правдивости этого указания), что функция gzseek, аналогичная fseek «эмулируется, но работает крайне медленно». Пришлось отказаться от прямого смещения указателя в файле архива на нужную позицию, заменив его на «пустое» чтение, в ущерб производительности. Если бы дело ограничивалось только архивами tar, этого можно было бы избежать.

Вот, собственно, и все. В результате, у меня получилась вполне универсальная библиотека, размером чуть больше 11 Кб не сжатого кода. Скачать библиотеку можно здесь: Archivator_tar-tar_gz.zip.

Источник

Description

This method creates the archive file and adds the listed files or directories.

If a file with the same tar name exists and is writable, it is replaced by the new tar archive (it is not an ‘add’, but a ‘create’). If a file exists and is write-protected or is a folder, the method raises a PEAR_Error.

Parameter

  • mixed $filelist — an array of filenames and directory names, or a single string with names separated by a single blank space. For each directory added in the archive, the files and sub-directories are also added.

Return value

boolean — Returns TRUE on success, FALSE on failure.

Throws

Possible PEAR_Error values

Error code Error message Reason Solution
NULL » Invalid file list « The argument for the function is not correct formatted or build. Check for typing mistakes in the argument

Note

This function can not be called statically.

Example

Creating an archive

$tar_object = new Archive_Tar ( «myArchive.tar» );

// print errors
$tar_object -> setErrorHandling ( PEAR_ERROR_PRINT );

// Archive content
$v_list [ 0 ]= «file.txt» ;
// the slash is optional
$v_list [ 1 ]= «data/» ;
$v_list [ 2 ]= «file.log» ;

// create the archive
$tar_object -> create ( $v_list );
?>

Creating a compressed archive, use a string as create() argument

$tar_object = new Archive_Tar ( «tarname.tgz» , true );
$tar_object -> setErrorHandling ( PEAR_ERROR_PRINT );
$tar_object -> create ( «file.txt data/ file.log» );
?>

Источник

Saved searches

Use saved searches to filter your results more quickly

You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.

pear/Archive_Tar

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Sign In Required

Please sign in to use Codespaces.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching GitHub Desktop

If nothing happens, download GitHub Desktop and try again.

Launching Xcode

If nothing happens, download Xcode and try again.

Launching Visual Studio Code

Your codespace will open once ready.

There was a problem preparing your codespace, please try again.

Latest commit

Git stats

Files

Failed to load latest commit information.

README.md

NOTE: This package is looking for a new maintainer. Are you interested? Send an e-mail to pear-dev@lists.php.net.

This package provides handling of tar files in PHP. It supports creating, listing, extracting and adding to tar files. Gzip support is available if PHP has the zlib extension built-in or loaded. Bz2 compression is also supported with the bz2 extension loaded. Also Lzma2 compressed archives are supported with xz extension.

Please report all new issues via the PEAR bug tracker.

Pull requests are welcome!

To test, run either $ phpunit tests/ or $ pear run-tests -r

To build, simply $ pear package

To install from scratch $ pear install package.xml

To upgrade $ pear upgrade -f package.xml

Источник

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