Архивация данных в PHP: методы сжатия и распаковки

Раздел: Работа с файлами -> Архивация ZIP

Введение в работу с ZIP архивами в PHP

PHP предоставляет мощный класс ZipArchive, который позволяет создавать, читать и модифицировать ZIP архивы. Далее рассмотрено основное решение и альтернативные подходы для различных задач.

Как создать ZIP архив и добавить в него файлы с помощью PHP?

Базовый сценарий: нужно упаковать один или несколько файлов в архив. Используется класс ZipArchive.


$zip = new ZipArchive();
$zipName = 'example.zip';
if ($zip->open($zipName, ZipArchive::CREATE) === TRUE) {
    $zip->addFile('file1.txt', 'file1.txt');
    $zip->addFromString('note.txt', 'Содержимое файла');
    $zip->close();
    echo 'Архив создан: ' . $zipName;
} else {
    echo 'Ошибка создания архива';
}
  

Php zip files (работа с zip файлами в php)

Пояснение: метод open с флагом ZipArchive::CREATE создаёт новый файл, если его нет. addFile добавляет уже существующий файл на диске, addFromString - данные из строки.

Типичная ошибка: отсутствие прав на запись в директории. Решение: проверить права chmod или указать полный путь к архиву.

Проблема: архив не создаётся, если уже существует файл с тем же именем без флага перезаписи. Флаг ZipArchive::OVERWRITE решает это.

Как создать защищенный паролем ZIP архив?

Для некоторых данных требуется шифрование. ZipArchive поддерживает парольную защиту (только для традиционного шифрования).


$zip = new ZipArchive();
$zip->open('secret.zip', ZipArchive::CREATE);
$zip->addFile('data.txt', 'data.txt');
// Установка пароля для всех последующих добавляемых файлов
$zip->setPassword('my_pass');
// Шифрование конкретного файла
$zip->setEncryptionIndex(0, ZipArchive::EM_AES_256, 'my_pass');
$zip->close();
  

Цель: защита конфиденциальной информации. Используется в финансовых системах, хранении паролей. Ограничение: не все утилиты могут открыть AES-256.

Ошибка: если не установить пароль до вызова setEncryptionIndex, файл не будет зашифрован. Решение: строгая последовательность вызовов.

Как извлечь только определённые файлы из ZIP архива?

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


$zip = new ZipArchive();
if ($zip->open('archive.zip') === TRUE) {
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $filename = $zip->getNameIndex($i);
        if (strpos($filename, '.txt') !== false) {
            $zip->extractTo('.', $filename);
            echo "Извлечён $filename\n";
        }
    }
    $zip->close();
}
  

Пояснение: метод getNameIndex возвращает имя файла по индексу. Фильтр по расширению позволяет извлечь только нужные файлы.

Проблема: извлечение в текущую директорию может перезаписать существующие файлы. Лучше использовать отдельную папку.

Как просмотреть список файлов внутри ZIP архива без распаковки?

Для быстрой проверки содержимого можно вывести оглавление.


$zip = new ZipArchive();
if ($zip->open('large.zip') === TRUE) {
    for ($i = 0; $i < $zip->numFiles; $i++) {
        echo "$i: " . $zip->getNameIndex($i) . " (" . $zip->statIndex($i)['size'] . " байт)\n";
    }
    $zip->close();
}
  

Используется для отладки, систем резервного копирования.

Ошибка: архив повреждён - open вернёт false. Проверять через getStatusString.

Как создать ZIP в памяти и отдать на скачивание без сохранения на диск?

Полезно для генерации архивов на лету, например, в веб-приложениях.


$zip = new ZipArchive();
$tmpFile = tempnam(sys_get_temp_dir(), 'zip');
if ($zip->open($tmpFile, ZipArchive::OVERWRITE) === TRUE) {
    $zip->addFromString('readme.txt', 'Hello');
    $zip->close();
    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="archive.zip"');
    readfile($tmpFile);
    unlink($tmpFile);
    exit;
}
  

Цель: мгновенная выдача архива пользователю. Важно удалить временный файл.

Проблема: при большом объёме данных временный файл может занять много места на диске. Альтернатива - потоковый вывод через ZipStream (не встроенный).

Как эффективно добавить большой файл в ZIP без загрузки всего содержимого в память?

Метод addFile читает файл по частям, не загружая его целиком. Это оптимально для больших объёмов.


$zip = new ZipArchive();
$zip->open('bigfiles.zip', ZipArchive::CREATE);
// Добавление файла размером 500 МБ
$zip->addFile('/path/to/huge_video.mp4', 'video.mp4');
$zip->close();
  

В отличие от addFromString, который требует полного содержимого в памяти, addFile использует потоковую передачу.

Ошибка: если файл повреждён или нет прав на чтение, addFile вернёт false. Необходима проверка.

Как добавить в ZIP целую директорию с сохранением структуры папок?

Для рекурсивного добавления папки используется итератор.


$zip = new ZipArchive();
$zip->open('folder.zip', ZipArchive::CREATE);
$dir = 'my_folder';
$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($files as $file) {
    $zip->addFile($file->getRealPath(), $file->getSubPathname());
}
$zip->close();
  

Цель: создание резервной копии проекта. Второй параметр сохраняет относительный путь.

Ошибка: при большом количестве файлов может не хватить памяти на итератор. Используйте DirectoryIterator с ручным перебором.

Расширенные примеры работы с ZIP в PHP

Пример 1: Сжатие с разными методами (store / deflate)

По умолчанию ZipArchive использует DEFLATE. Можно принудительно указать метод для каждого файла.

Пример

$zip = new ZipArchive();
$zip->open('mixed.zip', ZipArchive::CREATE);
$zip->addFile('bigfile.bin', 'bigfile.bin');
// Устанавливаем метод без сжатия (STORE) для определённого файла
$zip->setCompressionName('bigfile.bin', ZipArchive::CM_STORE);
$zip->addFromString('text.txt', 'compress me');
// Оставляем CM_DEFLATE по умолчанию
$zip->close();

Результат: файл bigfile.bin сохраняется без сжатия, что ускоряет добавление, но увеличивает размер архива.

(архив mixed.zip создан, bigfile.bin не сжат, text.txt - сжат)

Пример 2: Восстановление повреждённого ZIP и проверка целостности

ZipArchive может попытаться открыть архив с флагом ZipArchive::CHECKCONS для проверки.

Пример

$zip = new ZipArchive();
$res = $zip->open('corrupt.zip', ZipArchive::CHECKCONS);
if ($res === TRUE) {
    echo 'Архив корректен';
} else {
    echo 'Ошибка: ' . $zip->getStatusString();
    // Попытка восстановить - открыть без проверки
    $zip2 = new ZipArchive();
    if ($zip2->open('corrupt.zip') === TRUE) {
        echo 'Архив открыт, но может содержать ошибки';
    }
}

Результат: вывод сообщения о состоянии архива.

Ошибка: The archive is corrupt, but it could be opened

Пример 3: Извлечение с переименованием файлов

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

Пример

$zip = new ZipArchive();
$zip->open('images.zip');
$targetDir = '/tmp/extracted/';
for ($i = 0; $i < $zip->numFiles; $i++) {
    $originalName = $zip->getNameIndex($i);
    $newName = 'img_' . $i . '_' . basename($originalName);
    $zip->extractTo($targetDir, $originalName);
    rename($targetDir . $originalName, $targetDir . $newName);
}
$zip->close();

Результат: в папке extracted появятся файлы с новыми именами.

/tmp/extracted/img_0_photo.jpg, /tmp/extracted/img_1_logo.png

Пример 4: Работа с несколькими архивами - объединение и разделение

Можно открыть один архив, скопировать файлы в другой.

Пример

$zipSrc = new ZipArchive();
$zipSrc->open('source.zip');
$zipDst = new ZipArchive();
$zipDst->open('merged.zip', ZipArchive::CREATE);
for ($i = 0; $i < $zipSrc->numFiles; $i++) {
    $name = $zipSrc->getNameIndex($i);
    $content = $zipSrc->getFromIndex($i); // загружает в память
    $zipDst->addFromString($name, $content);
}
$zipSrc->close();
$zipDst->close();

Для больших файлов лучше использовать временные файлы или потоки. Результат - единый архив merged.zip.

Пример 5: Создание ZIP с использованием PHP-потоков (php://temp)

Можно создать архив без записи на диск, используя поток для временного хранения.

Пример

$zip = new ZipArchive();
$tmp = tmpfile();
$meta = stream_get_meta_data($tmp);
$path = $meta['uri'];
$zip->open($path, ZipArchive::OVERWRITE | ZipArchive::CREATE);
$zip->addFromString('hello.txt', 'World');
$zip->close();
rewind($tmp);
// Отправка в вывод
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="stream.zip"');
fpassthru($tmp);
fclose($tmp);

Результат: браузер получает ZIP без сохранения на диск сервера.

Пример 6: Получение комментария архива и файлов

ZipArchive поддерживает комментарии.

Пример

$zip = new ZipArchive();
$zip->open('commented.zip');
echo 'Комментарий архива: ' . $zip->getArchiveComment() . "\n";
$zip->setArchiveComment('Новый коммент');
$zip->close();

Результат: выводится старый комментарий, затем архив обновляется.

Комментарий архива: Старый комментарий

Пример 7: Обработка ошибок и исключения

Важно проверять возвращаемые значения.

Пример

$zip = new ZipArchive();
$status = $zip->open('notexist.zip');
if ($status !== true) {
    switch ($status) {
        case ZipArchive::ER_NOENT:
            echo 'Файл не найден';
            break;
        case ZipArchive::ER_OPEN:
            echo 'Не удалось открыть';
            break;
        default:
            echo 'Код ошибки: ' . $status;
    }
}

Результат: вывод сообщения об ошибке в зависимости от кода.

Файл не найден

Работа с ZIP файлами в PHP - comments

En
Php zip files (php)