Веб разработка
August 22, 2021
PHP и ZIP: Unicode в именах файлов
На одном проекте требовалось сохранять файлы с русскими именами в ZIP архив. Оказалось, что это не так и просто, как кажется с первого взгляда. Формат ZIP был разработан Филом Кацем (Phil Katz) в 1989 году. В те времена никто и не думал, что файловые имена могут быть не латинскими.
В 2001 году, после смерти Каца, вышла версия 4.5 спецификации, позволяющая добавлять в архив файлы, больше 4 Гб. В 2006 году появилась спецификация 6.3.0, которая поддерживает хранение имен файлов в кодировке UTF-8. Эта спецификация нам и интересна.
Как можно на PHP создать архив содержащий имена файлов на русском?
- Костыли. Windows XP не поддерживает юникод в именах файлов в ZIP, зато имена файлов можно перекодировать в кодировку WINDOWS-866. MacOS и Ubuntu поддерживает UTF-8 в именах файлов. По-идее можно для каждой операционной системы, но этот вариант самый плохой. Я его не советую.
- Использовать ext-zip в php >=8, начиная с этой версии класс ZipArchive научился работать и с большими файлами и с юникодом в именах файлов.
<?php
$zip = new ZipArchive();
$zip->open(__DIR__.'/1.zip', ZipArchive::CREATE);
$zip->addFile(__FILE__, 'Привет.php');
$zip->close();
?> - Использовать вместо ext-zip библиотеку. Библиотека для своей работы не требует расширение zip. Есть версия как для PHP >=5.5, так и более новая версия с под PHP 7.4. Последний способ мне больше всего, подключил пакет через composer и все.
// создание нового архива $zipFile = new \PhpZip\ZipFile(); try{ $zipFile ->addFromString('zip/entry/filename', "Is file content") // добавить запись из строки ->addFile('/path/to/file', 'data/tofile') // добавить запись из файла ->addDir(__DIR__, 'to/path/') // добавить файлы из директории ->saveAsFile($outputFilename) // сохранить архив в файл ->close(); // закрыть архив // открытие архива, извлечение файлов, удаление файлов, добавление файлов, установка пароля и вывод архива в браузер. $zipFile ->openFile($outputFilename) // открыть архив из файла ->extractTo($outputDirExtract) // извлечь файлы в заданную директорию ->deleteFromRegex('~^\.~') // удалить все скрытые (Unix) файлы ->addFromString('dir/file.txt', 'Test file') // добавить новую запись из строки ->setPassword('password') // установить пароль на все записи ->outputAsAttachment('library.jar'); // вывести в браузер без сохранения в файл } catch(\PhpZip\Exception\ZipException $e){ // обработка исключения } finally{ $zipFile->close(); }
Что стоит почитать: