Создание и управление архивными файлами в PHP

Раздел: Разработка на PHP -> Сжатие и архивация

Работа с архивами в PHP: обзор инструментов

Архивация файлов является повседневной задачей при разработке на PHP. Это может быть создание резервных копий, упаковка дистрибутивов, обработка загруженных архивов. В PHP существует несколько способов работы с форматами ZIP и TAR. Рассмотрим каждый из них с примерами кода и разбором типичных сложностей.

Основное решение: встроенный класс ZipArchive

Как создать ZIP архив на PHP без внешних библиотек?

Класс ZipArchive (расширение zip) предоставляет все необходимые методы для создания, модификации и извлечения ZIP архивов. Он встроен в PHP и не требует дополнительных установок, кроме включения расширения в php.ini (обычно включено по умолчанию).


$zip = new ZipArchive();
$zipName = '/tmp/example.zip';

if ($zip->open($zipName, ZipArchive::CREATE) === TRUE) {
    $zip->addFile('file1.txt', 'file1.txt');
    $zip->addFromString('hello.txt', 'Hello World');
    $zip->close();
    echo 'Архив создан: ' . $zipName;
} else {
    echo 'Ошибка создания архива';
}

файлы архив php (работа с архивами в php (zip, tar))

Пояснение шагов:

  1. Создаётся экземпляр ZipArchive.
  2. Метод open открывает архив на запись. Флаг ZipArchive::CREATE создаёт новый файл, если его нет.
  3. addFile добавляет существующий файл из файловой системы.
  4. addFromString добавляет данные из строки (полезно для динамического контента).
  5. close закрывает архив и сохраняет изменения.

Типичные ошибки и решения

  • ZipArchive не определён: расширение zip не установлено или не включено. Решение: проверить php.ini (extension=zip) или установить пакет php-zip.
  • Файл архива не открывается на запись: нет прав на запись в целевую директорию. Решение: установить права 755 или 777 для временной папки.
  • Добавление файла с пустым именем: метод addFile вернёт FALSE. Решение: перед добавлением проверять существование файла и корректность пути.

Вариант: PharData для работы с TAR архивами

Как создать TAR архив в PHP, если расширение phar доступно?

Класс PharData является частью расширения Phar и позволяет упаковывать файлы в форматы TAR (с возможностью сжатия Gzip или Bzip2). Он поддерживает как полные архивы, так и потоковое добавление.


$phar = new PharData('/tmp/example.tar');
$phar->addFile('file1.txt', 'file1.txt');
$phar->addFromString('hello.txt', 'Hello World');
// Для сжатия Gzip:
$phar->compress(Phar::GZ);

Метод compress создаёт файл example.tar.gz и удаляет несжатый исходник (если не указать Phar::NONE).

Проблемы:

  • Расширение Phar может быть отключено в целях безопасности. Решение: включить в php.ini (phar.readonly = Off).
  • Сжатие Gzip требует расширение zlib. Решение: установить php-zlib.
  • PharData работает только с TAR, не поддерживает ZIP напрямую.

Вариант: Использование системных утилит tar или zip

Как создать архив, используя внешние программы, если нужна полная совместимость с командной строкой?

Этот подход полезен, когда необходимо использовать продвинутые возможности оригинальных утилит (например, многотомные архивы, исключение по маске, сохранение прав доступа). Вызов происходит через функции exec, shell_exec или system.


$dirToArchive = '/path/to/dir';
$outputArchive = '/tmp/backup.tar.gz';
$command = "tar -czf " . escapeshellarg($outputArchive) . " -C " . escapeshellarg(dirname($dirToArchive)) . " " . escapeshellarg(basename($dirToArchive));
exec($command, $output, $status);
if ($status === 0) {
    echo "Архив создан успешно";
} else {
    echo "Ошибка: " . implode("\n", $output);
}

Риски и ограничения:

  • Безопасность: требуется тщательно экранировать аргументы (используйте escapeshellarg).
  • Зависимость от операционной системы. Команды tar/zip могут отсутствовать или иметь другой синтаксис (Windows).
  • Обработка вывода ошибок: необходимо проверять код возврата и сообщения.

Вариант: Сторонние библиотеки (Composer)

Как работать с архивами, если нужны расширенные возможности (например, поддержка RAR, 7z, или более удобное API)?

Библиотеки вида maennchen/zipstream-php, pear/archive_tar или splitbrain/php-archive предоставляют альтернативные решения, часто более гибкие или кроссплатформенные.


// Пример с maennchen/zipstream-php (создание ZIP на лету)
use ZipStream\ZipStream;
use ZipStream\Option\Archive;

$opt = new Archive();
$zip = new ZipStream('output.zip', $opt);
$zip->addFile('file1.txt', 'содержимое');
$zip->finish();

Эта библиотека особенно полезна для потоковой передачи больших архивов в ответе HTTP, так как не сохраняет весь архив в памяти.

Недостатки:

  • Требуют установки через Composer и могут иметь зависимости.
  • Некоторые библиотеки не поддерживают распаковку, только создание.

Выбор метода зависит от требований к проекту. Для большинства случаев достаточно встроенного ZipArchive. Для работы с TAR хорошо подходит PharData. Если нужна максимальная производительность или специфические форматы, следует рассмотреть внешние утилиты или специализированные пакеты.

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

Пример 1: Рекурсивное архивирование директории в ZIP

Как упаковать целую папку со всеми вложенными файлами и каталогами?

Пример

function archiveDirectory($sourceDir, $zip, $basePath = '') {
    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($sourceDir, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::LEAVES_ONLY
    );
    foreach ($files as $file) {
        $filePath = $file->getRealPath();
        $relativePath = $basePath . $file->getSubPathName();
        if ($file->isDir()) {
            $zip->addEmptyDir($relativePath);
        } else {
            $zip->addFile($filePath, $relativePath);
        }
    }
}

$zip = new ZipArchive();
if ($zip->open('archive.zip', ZipArchive::CREATE) === TRUE) {
    archiveDirectory('/path/to/folder', $zip);
    $zip->close();
    echo 'Архив создан';
} else {
    echo 'Ошибка';
}

Результат: файл archive.zip содержит иерархию исходной директории.

Пример 2: Создание ZIP архива в памяти (без записи на диск)

Как отправить архив пользователю непосредственно в ответе HTTP, не сохраняя временный файл?

Пример

$zip = new ZipArchive();
$tmpFile = tempnam(sys_get_temp_dir(), 'zip');
if ($zip->open($tmpFile, ZipArchive::CREATE) === TRUE) {
    $zip->addFromString('readme.txt', 'Этот архив создан в памяти');
    $zip->addFile('image.png', 'images/image.png');
    $zip->close();

    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="memory.zip"');
    header('Content-Length: ' . filesize($tmpFile));
    readfile($tmpFile);
    unlink($tmpFile);
    exit;
}

Результат: браузер предложит скачать файл memory.zip.

Пример 3: Установка пароля на ZIP архив (PHP 7.2+)

Как защитить архив паролем (шифрование AES-256)?

Пример

$zip = new ZipArchive();
if ($zip->open('protected.zip', ZipArchive::CREATE) === TRUE) {
    $zip->addFromString('secret.txt', 'Конфиденциальные данные');
    $zip->setPassword('my_strong_password');
    // Шифрование всех файлов
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $zip->setEncryptionName($zip->getNameIndex($i), ZipArchive::EM_AES_256);
    }
    $zip->close();
    echo 'Архив защищён паролем';
}

Обратите внимание: для работы шифрования требуется PHP собранный с libzip >= 1.2.0.

Пример 4: Чтение и извлечение файлов из TAR с помощью PharData

Как программно получить содержимое TAR архива и извлечь определённые файлы?

Пример

$tar = new PharData('example.tar');
// Вывести список файлов
foreach ($tar as $file) {
    echo $file->getPathname() . "\n";
}
// Извлечь все файлы в директорию
$tar->extractTo('/destination');
// Извлечь только один файл
$tar->extractTo('/destination', 'path/inside/archive/myfile.txt');

Вывод списка: перечисляются все пути внутри архива.

Пример 5: Создание tar.gz с прогрессом (комбинация внешней утилиты)

Как отображать прогресс создания большого архива через командную строку?

Пример

$source = '/var/www/html';
$target = '/backup/site.tar.gz';
$command = "tar -czf " . escapeshellarg($target) . " -C " . escapeshellarg(dirname($source)) . " " . escapeshellarg(basename($source));
$descriptorspec = [
    0 => ["pipe", "r"],
    1 => ["pipe", "w"],
    2 => ["pipe", "w"]
];
$process = proc_open($command, $descriptorspec, $pipes);
if (is_resource($process)) {
    // Закрываем stdin
    fclose($pipes[0]);
    // Читаем stderr (tar выдаёт предупреждения туда)
    $errorOutput = stream_get_contents($pipes[2]);
    fclose($pipes[2]);
    // stdout может быть пустым
    $stdout = stream_get_contents($pipes[1]);
    fclose($pipes[1]);
    $return_value = proc_close($process);
    if ($return_value === 0) {
        echo "Архив создан успешно.\n";
    } else {
        echo "Ошибка: $errorOutput\n";
    }
}

Результат: в директории /backup появится файл site.tar.gz.

Пример 6: Фильтрация файлов при архивации (исключение временных файлов)

Как добавить в ZIP только определённые типы файлов, исключая, например, .log и .tmp?

Пример

$zip = new ZipArchive();
$zip->open('filtered.zip', ZipArchive::CREATE);
$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator('/var/log', RecursiveDirectoryIterator::SKIP_DOTS)
);
$allowedExtensions = ['log', 'txt']; // измените на нужные
foreach ($files as $file) {
    if ($file->isFile()) {
        $ext = strtolower($file->getExtension());
        if (in_array($ext, $allowedExtensions)) {
            $zip->addFile($file->getRealPath(), 'logs/' . $file->getFilename());
        }
    }
}
$zip->close();
echo 'Архив с отфильтрованными файлами создан';

Пример 7: Работа с архивами через потоковую библиотеку zipstream-php

Как организовать потоковую передачу очень большого ZIP без загрузки в память?

Пример

require 'vendor/autoload.php';
use ZipStream\ZipStream;
use ZipStream\Option\Archive;
use ZipStream\Option\File;

$opt = new Archive();
$opt->setSendHttpHeaders(true);
$zip = new ZipStream('large.zip', $opt);

// Добавление большого файла из потока
$fileOpt = new File();
$fileOpt->setMethod(File::METHOD_DEFLATE);
$zip->addFileFromStream('bigfile.dat', fopen('/tmp/bigfile.dat', 'rb'), $fileOpt);

// Добавление из строки
$zip->addFile('notes.txt', 'Some notes');

$zip->finish();

Результат: браузер получит поток с архивом без временных файлов на сервере.

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

Работа с архивами в PHP (Zip, Tar) - comments

En
файлы архив php (php)