Архивация данных в PHP: методы сжатия и распаковки
Введение в работу с 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;
}
}
Результат: вывод сообщения об ошибке в зависимости от кода.
Файл не найден