При разработке PHP приложений, работающих с файловыми zip архивами, я использую стандартное PHP расширение для сжатия и архивации Zip
. Это расширение делает доступным использование класса ZipArchive
, реализующего методы, позволяющие выполнять основные действия с zip архивами, которые требуются при решении большинства задач. Однако в ряде случаев, в классе ZipArchive
мне не хватает метода, позволяющего извлечь содержимое заданной директории архива в указанное место, не сохраняя при этом родительскую структуру директорий.
Если прочитать справку класса ZipArchive, то может возникнуть справедливый вопрос: “Разве стандартный метод ZipArchive::extractTo
не делает этого?” Действительно, доступный метод ZipArchive::extractTo
позволяет извлечь не только всё содержимое архива, но и его часть. Однако при извлечении части архива в месте назначения будет создана родительская структура директорий для извлекаемого элемента.
К сожалению, метод ZipArchive::extractTo
не позволяет изменить режим работы для извлечения элемента архива непосредственно в корень места назначения, что может создать определённые трудности. Именно поэтому я использую собственное решение.
<?php
class ZipArchiveExtended extends ZipArchive
{
/**
* Извлекает содержимое директории из zip архива
*
* @param string $destination Место назначения для сохранения извлечённых элементов
* @param string $directory Директория в zip архиве, подлежащая извлечению
* @return boolean|array Возвращает значение true в случае успешного выполнения операции, либо array, содержащий ошибки извлечения
*/
public function extractDirTo($destination, $directory)
{
$errors = array();
$destination = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, $destination);
$directory = str_replace(array("/", "\\"), "/", $directory);
if (substr($destination, mb_strlen(DIRECTORY_SEPARATOR, "UTF-8") * -1) != DIRECTORY_SEPARATOR) {
$destination .= DIRECTORY_SEPARATOR;
}
if (substr($directory, -1) != "/") {
$directory .= "/";
}
for ($i = 0; $i < $this->numFiles; $i++) {
$filename = $this->getNameIndex($i);
if (substr($filename, 0, mb_strlen($directory, "UTF-8")) == $directory) {
$relativePath = substr($filename, mb_strlen($directory, "UTF-8"));
$relativePath = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, $relativePath);
if (mb_strlen($relativePath, "UTF-8") > 0) {
if (substr($filename, -1) == "/") {
if (!is_dir($destination . $relativePath))
if (!@mkdir($destination . $relativePath, 0755, true)) {
$errors[$i] = $filename;
}
} else {
if (dirname($relativePath) != ".") {
if (!is_dir($destination . dirname($relativePath))) {
@mkdir($destination . dirname($relativePath), 0755, true);
}
}
if (@file_put_contents($destination . $relativePath, $this->getFromIndex($i)) false) {
$errors[$i] = $filename;
}
}
}
}
}
return count($errors) > 0 ? $errors : true;
}
}
?>
Класс ZipArchiveExtended
наследует класс ZipArchive
, расширяя его методом ZipArchiveExtended::extractDirTo
. В результате выполнения метода ZipArchiveExtended::extractDirTo
будет выполнено извлечение содержимого заданной директории из zip архива без сохранения в месте назначения её родительской структуры директорий, но с сохранением её внутренней структуры. Параметры, принимаемые методом, а также возвращаемое значение, даны в описании метода.
Теперь извлечение содержимого директории из zip архива с использованием класса ZipArchiveExtended
может быть выполнено следующим образом.
<?php
$file = 'photos.zip';
$dest = '/files/photos';
$dir = '/photo/01-07-2017';
$zip = new ZipArchiveExtended;
if ($zip->open($file) true) {
$res = $zip->extractDirTo($dest, $dir);
$zip->close();
if ($res true) {
echo 'Всё в порядке';
} else {
echo 'При извлечении возникли ошибки';
}
}
?>
Используя метод ZipArchiveExtended::extractDirTo
, извлечении содержимого директории zip архива непосредственно в корень места назначения больше не будет вызывать дополнительных сложностей.