Итак мы подошли к первой реальной распаковке на PHP нашего архива. Нас интересует возможность закачки на сайт всего архива с ее распаковкой и размещением в дирректории на сервере.
Для того чтоб скачать архив с компьютера или считать его с другого сайта, нужно выполнить некоторые стандартные операции, которые широко освещаны в инете. Поэтому я сейчас не буду касаться этой темы, а все внимание заострю на моменте конкретной распаковки некого архива, который, на этот момент средствами штатной транспортировки файлов, доставлен на наш сервер. Также я не коснусь проблем конкретного размещения распакованого архива, и буду считать , что фолдер куда мне необходимо разместить его мне известен исходно.
Для чтения заголовков, которые записаны в виде двоичных данных, в PHP есть две замчательные функция, для работы с двоичными данными, разной длины и размещения. Это функции unpack(); pack(); . Для распаковки нам нужна конкретно unpack, которая не только изымает данные в необходимом нам формате, но и размещает их в предопределенном для нас именованом формате. Чтоб описать все форматы запишем следующий PHP-код
define("HDZ_FILE","Vid/vver/vflg/vcmp/vmtm/vmdt/
Vcrc/Vcsz/Vfsz/vnfln/vexln");
define("LEN_FILE",30);
define("SGN_FILE",0x04034b50);
Первая крнстанта HDZ_FILE задает формат и поля заголовка. Как видно из конкретики формат данных для функции unpack, следующий. Каждый ключ будущих распакованых двоичных данных в массив определяется длиной+типом и именем , разделенное иежду собой прямым слэшем (/). Первая буква каждого ключа определяет тип и формат поля. Не вдаваясь в подробности форматов для этой функции (в принципе это заслуживает отдельной статьи) скажу, что здесь используется только два формата V и v, которые соответственно определяют , что двоичные данные хранятся в виде четырех и двух байтовых слов, причем последовательность их определяется от младшего байта к старшему.
Вторая константа LEN_FILE определяет длину заголовка в байтах, а третья SGN_FILE конкрктно его сигнатуру.
Чтобы прочитать одну запись из архивированого файла была определена функция:
function _zip_item(&$zip){
Эта функция читает из файла определенного ссылкой на дескриптор $zip
if($dat=@fread($zip,LEN_FILE)){
Читаем из файла $zip заголовок ввиде цепочки байт длиной определенной константой LEN_FILE
$itm = unpack(HDZ_FILE,$dat);
Если чтение успешно, то функцией unpack по формату распаковываем его в масив
if($itm['id']!=SGN_FILE) return -1;
Проверяем заголовок на наличии необходимой сигнатуры
если нет, то это не наши данные , и возвращаем -1.
if($itm['nfln'])
$itm['name'] = @fread($zip,$itm['nfln']);
Если поле nfln не равно нулю, то читаем из файла $zip - имя нашего файла
if($itm['exln'])
$itm['exta'] = @fread($zip,$itm['exln']);
Также читаем экстра-данные, если они есть.
Все впринципе необходимая нам информация для распаковки имеется
Далее при наличии их - читаем из файла
if($itm['csz']){
$d = @fread($zip,$itm['csz']);
и при не нулевом значения в поле cmp выполняем распаковку данных
$itm['dat'] = $itm['cmp']? @gzinflate($d):$d;
}
Вот в принципе и все.
Осталось за малым, это определить тип записи, а конкретно это файл или директория.Однозначный алгоритм здесь может быть разным, но для простоты я считаю, что перед нами директория если в конце имени присутствует прямой слэш, шаблон которого задается через нерегулярное выражение END_SLASH, как "/$".
$itm['type'] = 0;
if(ereg(END_SLAH,$itm['name'])){
$itm['name'] = ereg_replace(
END_SLAH,"",$itm['name']);
$itm['type'] = 1;
}
return $itm;
результат работы выдается через возврат функции
}
return 0;
В противном случае возвращается 0 - как ошибка.
}
Теперь приведем основную функцию, которая использует функцию чтения и распаковки единицы данных выполняет полноправное действа со всем файлом , включая чтение распаковку и размещение результатов ее вместе с генерацией всех необходимых дирректориев.
function unzip($src,$to=0){
Вот эта функция, которая и будет выполнять конкретную распаковку. Сразу оговоримся, что функция может быть и другой. Параметры функции определяются так. $src - это полный путь до нашего исходного архива, а $to - это полный путь до места куда наш архив будет распаковываться. Этот параметр инмеет значение по умолчанию равный 0, что означает условие отсутствия перенесения распакованых данных на диск, оставляя их в памяти, передавая через параметр возвращения функции. Такой способ управления назначением, позволяет оперативно управлять полученой информацией.
$dst = array();
Усьанавливаем начальный массив для информации в нуль
if($fnm = fopen($src,"rb")){
Открываем файл источник, получив его файловый дескриптор
while(is_array($d=_zip_item($fnm))){
читаем по порядку все элементы ZIP-архива
if($to){
и при наличии необходимости размещения
$pth = "$to/{$d['name']}";
формируем полный путь, куда будут размещены текущие распакованые данные
if($d['type']) @mkdir($pth);
Если запись определена как дирректория - то создаем ее
elseif($out=@fopen($pth,"wb")){
в противном случае - создаем файл для записи с именем полученым из архива
if(@fwrite($out,$d['dat'],$d['fsz']))
и сохраняем данные в конкретный файл
unset($d['dat']);
и при успешной записи в файл обнуляем содержимое памяти, уничтожив элемент массива
@fclose($out);
}
$d['dat'] = "to OK!";
при этом сделаем отметку - что данные дошли до места назначения
}
$dst[] = $d;
пополняем пул выходных данных результатом работы
}
@fclose($fnm);
Окончательно закрываем источник, в конце работы
}
if($d<0) return $dst;
если последняя сигнатура была не наша, то это означает конец работы
return 0;
}
Вот в принципе и все....
Пример вызова функции
include("$_libr/unzip.php");
unzip("/tmp/11.zip","/tmp/zip");
И можно попить кофейка...
Файлы по теме статьи здесь