Php прочитать файл из архива

Обновлено: 03.07.2024

Расширение архива Zip в PHP используется для работы с Zip архивами и файлами, которые в них содержатся. Для использования этого расширения в PHP 5 необходимо активировать php_zip.dll внутри файла php.ini. В этой статье вы узнаете о том, как работать с файлами Zip архивов в PHP, используя ряд PHP демо-приложений. Вы научитесь: создавать Zip архивы;

  • создавать Zip архивы;
  • добавлять файлы и папки в архив из строки и данной дорожки;
  • удалять и переименовывать файлы, используя их индексы и имена;
  • составлять список характеристик объектов ZipArchive (количество файлов, имя файла, комментарии, statusSys и т. д.).

Создание Zip архивов в PHP и добавление файлов

Для создания Zip архива в PHP можно использовать предопределенную константу ZIPARCHIVE::CREATE; архив будет создан, если он еще не существует. (Для добавления файлов внутрь архива можно использовать вышеописанные методы addFile и addFromString.) Первое демо-приложение (add_file_from_string.php) создает (если таковой еще не существует) или открывает Zip архив archive1.zip, а после добавляет в него файл, используя метод addFromString():

bool ZipArchive::addFromString ( string $localname , string $contents ): Добавить файл в Zip-архив, используя его содержание.

Здесь код для add_file_from_string.php:

С результаты выполнения add_file_from_string.php вы также можете ознакомиться на Рисунке 1.

Файловый текст test1.txt был успешно добавлен в archive1.zip

Рис.1 The archive1.zip

Рис.1 The archive1.zip

Второе демо-приложение (add_file_directory.php) также использует архив archive1.zip для печати всех характеристик объекта ZipArchive (статус, statusSys, numFiles, название файла, комментарии или специальные параметры, в нашем случае архивный комментарий и количество файлов), чтобы добавить пустой каталог под названием Subdirectory1. Это приложение также использует архив archive1.zip для добавления нового файла, используя метод addFile. Ниже содержатся прототипы методов addFile() и addEmptyDir():

bool ZipArchive::addFile ( string $filename [, string $localname ] ) : добавляет файл в Zip-архив с даного пути bool ZipArchive::addEmptyDir ( string $dirname ) : добавляет пустую папку в архив.

Здесь код для add_file_directory.php:

С результатом листинга add_file_directory.php вы также можете ознакомиться на Рисунке 2:

ZipArchive Object ( [status] => 0 [statusSys] => 0 [numFiles] => 2 [filename] => D:\Apache Group\Apache2\htdocs\php\ZIP\archive1.zip [comment] => PHP ZIP ARCHIVE ) Создан новый каталог Комментарий: PHP ZIP ARCHIVE № файла:4

Характеристики объекта archive2.zip

Рисунок 2. Характеристики объекта archive2.zip

Следующее демо-приложение служит для создания Zip архива archive2.zip, который включает два файла: test3.txt и test4.txt. (Информацию о том, как пользоваться этим приложением, вы сможете найти ниже.) Код для archive2.php:

Результат archive2.php. Вы также можете ознакомиться с результатами на Рисунке 3: Файловый текст test3.txt успешно добавлен в archive2.zip при использовании метода addFile Файловый текст test4.txt успешно добавлен в archive2.zip при использовании метода addFromString method.

Содержание archive2.zip

Рисунок 3. Содержание archive2.zip

Извлечение Zip архива в PHP

Демо-приложение этой части (extract_archives.php) показывает, как извлекать содержимое архива в специальную папку, используя метод extractTo():bool ZipArchive::extractTo ( string $destination [, mixed $entries ] ) : извлечь содержимое архива.

Архивы archive1.zip и archive2.zip будут извлечены в папку archive, как показано на Рисунке 4. Код для extract_archives.php:

Результат extract_archives.php: archive1.zip и archive2.zip были извлечены в папку archive!

Содержимое папки archive после извлечения archive1 и archive2

Рисунок 4. Содержимое папки archive после извлечения archive1 и archive2

Следующее демо-приложение (extract_to_specified_folder.php) извлекает содержимое Zip архива в специальную папку, используя вышеописанный метод extractTo(). Код для extract_to_specified_folder.php:

Результат extract_to_specified_folder.php. Вы также можете ознакомиться с результатами на Рисунке 5: Архив извлечен в папку ZIP_extract!

Папка ZIP_extract с файлами archive1

Рисунок 5. Папка ZIP_extract с файлами archive1

С результатами листинга filelist.php вы также можете ознакомиться с на Рисунке 6: Файлы test3.txt и test4.txt из архива archive2.zip успешно извлечены в указанный каталог ZIP_TEST!

Выбранные файлы, извлеченные из archive2 в назначеный каталог ZIP_TEST

Рисунок 6.Выбранные файлы, извлеченные из archive2 в назначеный каталог ZIP_TEST

Получение характеристик объекта на основе его индекса

Для получения характеристик отдельного файла можно использовать метод statIndex:

mixed ZipArchive::statIndex ( int $index [, int $flags ] ).

Следующее демо-приложение (statIndex.php) итерирует список файлов в archive2.zip и печатает характеристики для каждого из объектов. Код для statIndex.php:

Результат листинга 5.php:

Следующее демо-приложение (locate.php) также позволяет получать характеристики файла, при условии что archive1.zip содержит этот файл. Метод locateName возвращает индекс файла в архиве и использует предопределенную константу ZIPARCHIVE::FL_NODIR, которая игнорирует компонент каталог. Прототип константы ZIPARCHIVE::FL_NODIR :

mixed ZipArchive::locateName ( string $name [, int $flags ] )

Код для locate.php:

Результат листинга locate.php:

Array ( [name] => test2.txt [index] => 2 [crc] => -513033757 [size] => 50 [mtime] => 1269715222 [comp_size] => 49 [comp_method] => 8 )

Удаление и переименование Zip архивов в PHP

Для удаления или переименования Zip архивов в PHP можно использовать имя или индекс. Методы, которыми можно воспользоваться для этой цели, включают:

bool ZipArchive::deleteIndex ( int $index ) – Удаляет объект в архиве, используя индекс; bool ZipArchive::deleteName ( string $name ) – Удаляет объект в архиве, используя имя; bool ZipArchive::renameIndex ( int $index , string $newname ) – Переименует объект на основе индекса; bool ZipArchive::renameName ( string $name , string $newname ) – Переименует объект на основе имени.

Следующее демо-приложение (rename.php) использует все вышеперечисленные методы для переименования файла с "index=3", как "renameByIndex.txt", и "test4.txt", как "renameByName.txt". Код для rename.php:

Результаты листинга rename.php. Вы также можете ознакомиться с результатами на Рисунке 7 и Рисунке 8: Файлы успешно переименованы в архиве archive2.zip!

Исходное содержимое archive2.zip

Рисунок 7. Исходное содержимое archive2.zip

Содержание архива archive2.zip после того, как файлы были переименованы

Рисунок 8. Содержание архива archive2.zip после того, как файлы были переименованы

Следующее приложение (delete.php) удаляет из archive2.zip файл с "index=1" и текстовый файл renameByIndex.txt, используя методы deleteIndex() и deleteName(). Код для delete.php:

В связи с тем, что архив содержал всего два файла и оба были удалены, archive2.zip был также удален. Следующее приложение (add_text_files.php) итерирует архивные файлы и добавляет все файлы .txt из настоящего каталога в text_archive.zip. Код для add_text_files.php:

Результаты листинга add_text_files.php: text_archive.zip успешно создан!

Группировка всех текстовых файлов из текущего каталога в новый архив text_archive.zip

Рисунок 9. Группировка всех текстовых файлов из текущего каталога в новый архив text_archive.zip

Заключение

Как вы, наверное, заметили, изучая приложения, представленные в статье, работать с расширением архива Zip в PHP довольно просто и даже забавно. Все, что от вас требуется, это умение обращаться с методами расширения. В этом случае вы с успехом сможете осуществлять различные операции с Zip архивами.

А теперь давайте на минутку отвлечемся и вспомним, из чего состоит наш архив: сначала идет набор данных упакованных файлов, где каждый упакованный файл предварён структурой Local File Header (LFH), после всех данных у нас идет набор структур Central Directory File Header (CDFH) — это такое оглавление по нашему архиву, в котором перечислены все элементы и позиции их смещения относительно начала файла. А завершает архив End Of Central Directory Record (EOCD) — тут указана позиция начала структур CDFH, их количество и общая длина в байтах. Поэтому архив следует читать с конца, чтоб сначала найти EOCD, потом прочесть структуры CDFH и таким образом получить список файлов в архиве.


Теперь попробуем найти структуру EOCD. Минимальная её длина, если отсутствует комментарий, будет 22 байта, из которых первые 4 это сигнатура. Поэтому сначала мы смещаемся на позицию filesize($file) — 22, читаем следующие 4 байта и, если нам повезло и эти байты равны сигнатуре (0x06054b50), значит это и есть наша EOCD. Если не повезло — побайтово двигаемся к началу файла, пока не найдем или не закончится файл — тогда, наверное, это не архив.


Теперь мы знаем сколько у нас элементов в архиве и где начинается «оглавление» — список CDFH структур. Мы можем их перебрать и получить имена, размер данных и позицию начала LFH для каждого из элементов архива.


Вообще бегать по файлу туда-сюда для каждой из записей — не самая лучшая идея с точки зрения быстродействия, поэтому я бы рекомендовал прочесть все CDFH структуры, а потом уже перейти перейти к чтению LFH и данных, если это необходимо, но у нас тут «Нетрадиционное программирование», поэтому нам можно.

В результате работы скрипта мы должны получить информацию об архиве примерно следующего вида:


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

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

Поэтому, я думаю, что основной цикл мы можем считать оконченным. Если у вас остались вопросы или я вспомню какие-то упоминания в комментариях к предыдущим статьям, то, возможно, будут некоторые статьи-дополнения.

Уже долгие годы самым распространенным форматом сжатия данных, является формат ZIP. Данный формат широко используется в разработках под web. Поэтому многие языки для web-программирования имеют либо встроенные средства или возможности подключения необходимых библиотек для работы с zip-архивами.

Непосредственно в самом PHP функций для распаковки и создания zip-архивов нету. Хотя это зависит, от вариантов его сборки. Но они присутствуют в PHP расширении “php_zip”. И именно оно позволяет работать с архивными zip-файлами.

Распаковка архива

С распаковкой архивов при веб разработке, приходиться сталкиваться наиболее часто, нежели при разработке прикладного ПО. Особенно когда возникает необходимость в пакетной загрузке данных (документы, сертификаты и т.д.). Ведь даже диалоговое окно для открытия файла в браузере, не имеет возможности мульти выбора файлов. Конечно, можно воспользоваться каким-либо flash-загрузчиком, но во многих ситуациях это не подходит. А значит остается всего один вариант – архивация данных. Для работы по распаковке архива есть ряд функций встроенных в расширение php_zip:

    void zip_close (resource $zip )

Закрывает архивный zip-файл. Параметр zip обязан быть zip-архивом, открытым до этого функцией zip_open().

Закрывает вхождение директории, специфицированное параметром zip_entry . Параметр zip_entry обязан быть правильным вхождением директории, открытым функцией zip_entry_open().

Возвращает фактический размер вхождения директории zip_entry. Параметр zip_entry обязан быть правильным вхождением директории, открытым функцией zip_read().

Возвращает имя вхождения директории zip_entry. Параметр zip_entry обязан быть правильным вхождением директории, открытым функцией zip_read().

Открывает вхождение директории в zip-файле для чтения. Параметр zip это правильный дескриптор ресурса, возвращённый функцией zip_open(). Параметр zip_entry это ресурс вхождения директории, возвращённый функцией zip_read(). Необязательный параметр mode может быть одним из режимов, специфицированных в документации для fopen().

Примечание: в настоящее время mode игнорируется и всегда имеет значение “rb”.Это из-за тог, что zip поддерживается в PHP с доступом только для чтения. Возвращает true при успехе, false при неудаче. В отличие от fopen() и других подобных функций, возвращаемое значение функции zip_entry_open() указывает только на результат операции и не нужно для чтения или закрытия вхождения директории.

Читает до length байтов из открытого вхождения директории. Если параметр length не специфицирован, Функция zip_entry_read() пытается прочитать 1024 байта. Параметр zip_entry является правильным вхождением директории, возвращённым функцией zip_read(). Возвращает прочитанные данные, или false, если достигнут конец файла.

Примечание: параметр length должен быть несжатым размером, который вы хотите прочитать.

Открывает новый zip-архив для чтения. Параметр filename это имя файла открываемого zip-архива. Возвращает дескриптор ресурса для дальнейшего использования в zip_read() и zip_close(), или возвращает false, если filename не существует.

Параметр zip_entry обязан быть правильным вхождением директории, открытым функцией zip_read(). Возвращает сжатый размер вхождения директории zip_entry.

Параметр zip_entry обязан быть правильным вхождением директории, открытым функцией zip_read(). Возвращает метод сжатия для вхождения директории zip_entry.

Ниже приведен найденный и модифицированный мной исходный код функции для распаковки архива. Данная функция производит распаковку архива в корневой каталог. Поэтому если есть необходимость в создании директории для последующего расположения в ней распакованных файлов и каталогов, то ее требуется слегка изменить.

Выше был продемонстрирован классический пример распаковки архива. Обратите внимание на то, что указанный к архиву путь, должен быть абсолютным. Но тем не менее существует еще одна возможность, чтобы его распаковать. Для этого нужно прибегнуть к помощи методов класса ZipArchive. Этот класс находится все в том же расширении “php_zip”. Итак для того чтобы применить другой вариант распаковки, необходимо написать следующий код:

Для распаковки архива у данного класса используется только один метод:

Будущая директория местонахождения распакованного архива задается в параметре $destination . Параметр $entries содержит элементы для извлечения. Он является необязательным и может принимать как одно значение, так и массив значений.

Второй вариант выглядит намного красивее и компактнее, чем первый, не так ли? Поэтому я свой выбор остановил именно на нем. И напоследок протестируем оба варианта на скорость распаковки архива объемом в 205Mb:

Создание архива

Создание архива происходит сложнее, чем его распаковка. Если конечно требуется создать архив с одним файлом или одной директорией, то здесь все просто. А вот если упаковывать директории с неограниченным уровнем вложенности каталогов, то здесь уже придется немного подумать. Во-первых, необходим хороший рекурсивный алгоритм для обхода директорий. Во-вторых, нужно дополнительно хранить локальное имя файла/каталога. Итак, для создания архива нам понадобятся четыре метода класса ZipArchive:

    bool addEmptyDir (string $dirname )

Добавляет в архив пустую директорию. Параметр dirname должен содержать имя директории. Метод в случае успеха возвращает true или false в противном случае.

Добавляет в архив файл, который находится по указанному в параметре filename пути. Параметр localname отвечает за имя файла в архиве. И если он указан, то параметр filename будет переопределен. Параметры start и length , зарезервированы для будущих целей. Данный метод так же в случае успеха возвращает true или false в случае ошибки.

Данный метод необходим для открытия нового архива с целью: чтения, записи или создания. Параметр filename должен содержать имя архива. Необязательный параметр flags используется в качестве режима открытия файла (ZIPARCHIVE::OVERWRITE, ZIPARCHIVE::CREATE, ZIPARCHIVE::EXCL, ZIPARCHIVE::CHECKCONS). Метод возвращает true в случае успеха или код ошибки (см. предопределенные константы ошибок).

Этот метод закрывает открытый или созданный архив и сохраняет изменения. Данный метод автоматически вызывается в конце сценария.

Ниже приведен исходный код созданного класса, позволяющего производить создание zip-архивов:

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

    bool ToZip (string $source , string $destination )

Создает zip-архив. В параметре source требуется указать путь к каталогу или файлу, который требуется запаковать. А в качестве параметра destination передается имя будущего архива. Метод возвращает true в случае успеха или false в случае возникновения ошибки.

PhpZip - php библиотека для продвинутой работы с ZIP-архивами.

Code Coverage

  • Открытие и разархивирование ZIP-архивов.
  • Создание ZIP-архивов.
  • Модификация ZIP-архивов.
  • Чистый php (не требуется расширение php-zip и класс \ZipArchive ).
  • Поддерживается сохранение архива в файл, вывод архива в браузер или вывод в виде строки, без сохранения в файл.
  • Поддерживаются комментарии архива и комментарии отдельных записей.
  • Получение подробной информации о каждой записи в архиве.
  • Поддерживаются только следующие методы сжатия:
    • Без сжатия (Stored).
    • Deflate сжатие.
    • BZIP2 сжатие при наличии расширения php-bz2 .

    Внимание!

    Для 32-bit систем, в данный момент не поддерживается метод шифрование Traditional PKWARE Encryption (ZipCrypto) . Используйте метод шифрования WinZIP AES Encryption , когда это возможно.

    • Установка пароля для чтения архива глобально или для некоторых записей.
    • Изменение пароля архива, в том числе и для отдельных записей.
    • Удаление пароля архива глобально или для отдельных записей.
    • Установка пароля и/или метода шифрования, как для всех, так и для отдельных записей в архиве.
    • Установка разных паролей и методов шифрования для разных записей.
    • Удаление пароля для всех или для некоторых записей.
    • Поддержка методов шифрования Traditional PKWARE Encryption (ZipCrypto) и WinZIP AES Encryption (128, 192 или 256 bit) .
    • Установка метода шифрования для всех или для отдельных записей в архиве.
    • PHP 7.4 или ^8.0 (предпочтительно 64-bit).
    • Опционально php-расширение bzip2 для поддержки BZIP2 компрессии.
    • Опционально php-расширение openssl для WinZip Aes Encryption шифрования.

    composer require nelexa/zip

    Последняя стабильная версия:

    Другие примеры можно посмотреть в папке tests/ .

    Запись в ZIP-архиве (Zip Entry) - файл или папка в ZIP-архиве. У каждой записи в архиве есть определённые свойства, например: имя файла, метод сжатия, метод шифрования, размер файла до сжатия, размер файла после сжатия, CRC32 и другие.

    Читайте также: