Все об php tar gz

Парсер Хабра

Как это часто случается, все началось с того, что мне потребовалось нечто, позволяющее обрабатывать архивы «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 (кому интересно, вот тут хорошо расписан заголовок блока информации) и заняться изучением кода. Результат привожу ниже. Понимаю, что задача специфичная, но возможно, кому-то и пригодится.

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

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

100 байт name — название (может содержать относительный путь);

12 байт size — размер файла, байт (кодирована в восьмеричной системе)

12 байт mtime – дата и время последней модификации в секундах эпохи UNIX (кодирована в восьмеричной системе)

8 байт chksum – контрольная сумма заголовка (не файла!)

1 байт typeflag – определяет файл у нас, или каталог: файл – 0, каталог — 5

100 байт linkname – ссылка на файл

— дальше – поля «нового» формата — 6 байт magic – содержит слово «ustar», т.е. признак «нового» формата

Читайте также:  How to make colors in css

2 байт version – версия нового формата (может отсутствовать)

32 байт uname – имя владельца

32 байт gname – имя группы владельца

8 байт devmajor – старший байт кода устройства

8 байт devminor – младший байт кода устройства

155 байт prefix – префикс (расширение) имени

Не используемые байты должны быть пустыми, хотя допускается код «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; $itarmode == «tar») @fputs($this->tarfile, $fr); else @gzputs($this->tarfile, $fr); > fclose($infile);

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

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

Читайте также:  Демонстрация возможностей CSS

$ffs = filesize($filename); if($ffs > 512) $tolast = 512 — fmod($ffs, 512); else $tolast = 512-$ffs; if($tolast != 512 && $tolast != 0)

Результат – архив в формате «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.

Читайте также:  Сохранение web страницы html

This entry passed through the Full-Text RSS service — if this is your content and you’re reading it on someone else’s site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers.

Источник

Работа с архивами tar.gz в php

Совсем недавно, во время написания небольшого сервиса, мне понадобилось средство для работы с архивами tar.gz. Гугл, вопреки ожиданиям, ничего внятно-понятного предложить не смог. Использовать exec почему-то не хотелось.. Было принято решение выдернуть нужный функционал из Битрикса, благо с архивами он работает довольно неплохо.

Битрикс, поделись кодом:)

Итак, первым делом копируем нужный нам php-файл. Он называется tar_gz.php и лежит в \bitrix\modules\main\classes\general\ Дополнительных скриптов класс не подтягивает, поэтому можно смело забирать только его.

Из всего набора возможностей, содержащихся в классе CArchiver, использовать будем только 2 метода — для архивирования и распаковки группы файлов.

Конструктор класса CArchiver выглядит так

function CArchiver($strArchiveName, $bCompress = false, $start_time = -1, $max_exec_time = -1, $pos = 0, $stepped = false)

Где
$strArchiveName — имя создаваемого архива или путь до существующего
$bCompress — флаг сжатия для нового архива

Остальные параметры используются в пошаговом режиме и рассматриваться в рамках этой статьи не будут

Архивирование

Для создания архива применим метод

function Add($vFileList, $strAddPath = false, $strRemovePath = false)

Где
$vFileList — путь до файла (или каталога), который надо архивировать
$strAddPath — строка, которая будет добавляться в начало пути архивируемых объектов (пояснение ниже:)
$strRemovePath — часть пути $vFileList, которая не будет помещена в архив

Например, у нас есть каталог future/mega/arc, который надо запаковать в result/test.tar.gz. При этом внутри архива future/mega/ быть не должно, вместо этого должно получиться new_folder/new/arc

require_once('tar_gz.php'); // имя архива $tmpfname = "result/test.tar.gz"; // содержимое этого каталога будем архивировать $folder = "future/mega/arc"; // создаем объект, флаг сжатия установлен $oArchiver = new CArchiver($tmpfname, true); // делаем архив, убираем future/mega/, добавляем new_folder/new $tres = $oArchiver->add('"'.$folder.'"', "new_folder/new", "future/mega/"); // вывод ошибок если есть $arErrors = &$oArchiver->GetErrors(); if(count($arErrors)>0) < $strError = "
"; foreach ($arErrors as $value) $strError .= "[".$value[0]."] ".$value[1]."
"; > else $strError = ".
"; echo $strError;

Разархивирование

Для распаковки используется метод

function extractFiles($strPath, $vFileList = false)

Где в качестве первого параметра указываем путь к каталогу, куда надо извлечь файлы.

Например, распаковываем result/test.tar.gz в yes/my/arc/extract/

require_once('tar_gz.php'); // имя архива $tmpfname = "result/test.tar.gz"; // куда распаковывать $folder = "yes/my/arc/extract"; // создаем объект $oArchiver = new CArchiver($tmpfname); // поеееехали! $tres = $oArchiver->extractFiles($folder); // вывод ошибок если есть $arErrors = &$oArchiver->GetErrors(); if(count($arErrors)>0) < $strError = "
"; foreach ($arErrors as $value) $strError .= "[".$value[0]."] ".$value[1]."
"; > else $strError = ".
"; echo $strError;

Вот так, быстро, удобно, но не совсем лицензионно можно работать с tar.gz архивами в php.

PS: если Вы никогда не работали с битриксом и не знаете где взять tar_gz.php — процесс выглядит примерно так: ищем в сети bitrix 7.5+ (ниже не проверял) в исходных кодах (в сети полно) и прямо из архива выдергиваем этот файл.

Источник

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