вторник, 17 марта 2009 г.

ZIP средствами PHP. Форматы

Формат и структура ZIP-архива.


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

Итак начинаем
Zip-архив состоит из трех крупных частей, каждая из которых выполняют свою роль во всем массиве данных


  1. Первая часть представляет собой массив спакованых данных . Имено в ней и расположены все части необходимой для нас информации.

  2. Вторая часть выполняет роль каталога архива, в которой расположены все описания и информация о включнном в архив файла.

  3. Последняя часть представляет собой заголовок , описывающий непосредственно сам ZIP-архив, а также в ней расположен комментарий к архиву, в котором сохраняется информация об архиве. Комментарий может отсутствовать. Вернее он практически всегда отсутвует, но его наличие вернее его отсутствие с возможным присутствием только осложняет жизнь программисту.


Каждая из частей начинается уникальным идентификатором, который однозначно определяет часть архива. Этот идентификатор расположен всегда в начале любой записи.
Нашу информацию мы начнем со структуры последней часть архива, которая называется глобальный заголовок. Этот заголовок представляет собой 22-байтную запись, расположенную в конце файла архива, сразу перед комметарием.
Вот его расшифровка.

  1. 4:06054B50: - идентификатор этого заголовка.

  2. 2:disk: - номер диска

  3. 2:disk_start: - номер диска начального

  4. 2:disk_entries: - число файлов на диске

  5. 2:entries: - число файлов в ахиве

  6. 4:size: - размер в байтах дирректории архива

  7. 4:offset: - смещение до директории архива т начала файла архива

  8. 2:comment_size: - размер следующего за этими двумя байтами комментарием.


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

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

  1. 4 : 02014B50 : - идентификатор этого заголовка.

  2. 2 : version : - версия архива (2.0)

  3. 2 : version_extracted : - версия паковки (1.0)

  4. 2 : flag : - флаг

  5. 2 : compression : - указатель на степень сжатия (0-не сжат)

  6. 2 : mtime : - время последнеймодификации объекта

  7. 2 : mdate : - дата последней модификации объекта

  8. 4 : crc : - циклический контрольный код сrс32

  9. 4 : compressed_size : - размер объекта в байтах после паковки

  10. 4 : size : - размер объекта в байтах исходно

  11. 2 : filename_len : - длина имени файла

  12. 2 : extra_len : - размер дополнительных данных

  13. 2 : comment_len : - размер комментарием.

  14. 2 : disk : - номер носителя.

  15. 2 : internal : - уровень вложения

  16. 4 : external : - внешние данные (10-фолдер)

  17. 4 : offset : - смещенние от начала файла до объекта.


За этим заголовком непосредственно располагаются данные , которые определяются этим заголовком, а имено {имя файла в байтах длиной по полю 11}, {байты из поля 12},{коментарий по полю 13}.
Используя этот блок записей мы однозначно строим структуру архика. Число записей этого блока выбирается из поля 5 блока с сигнатурой 0605..

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

  1. 4 : 04034B50 : - идентификатор этого заголовка.

  2. 2 : version : - версия архива (2.0)

  3. 2 : flag : - флаг

  4. 2 : compression : - указатель на степень сжатия (0-не сжат)

  5. 2 : mtime : - время последнеймодификации объекта

  6. 2 : mdate : - дата последней модификации объекта

  7. 4 : crc : - циклический контрольный код сrс32

  8. 4 : compressed_size : - размер объекта в байтах после паковки

  9. 4 : size : - размер объекта в байтах исходно

  10. 2 : filename_len : - длина имени файла

  11. 2 : extra_len : - размер дополнительных данных


и Следующих за ним сначала {имя файла в байтах длиной по полю 10}, {байты из поля 11}
и самих данных размером в байтах из поля 8. Наличие паковки определяется либо неравенством полей 8 и 9, но однозначно - не нулевым значением поля 4.

Вот в принципе и все. Здесь уместно сделать несколько замечаний . Как видно из структуры заголовков для данных и дирректории, проглядывается приличное дублирование данных, которое позволяет независимо распаковывать архив, не копаясь в дирректории, а просто сканировать архив с начала файла, поочедно расифровывая сигнатуры данных, считывая как сами данные, так и их заголовки. Такая структура архива позволяет заметно ускорить процес распаковки, применяя меньше затрат для этой, не совсем простой операции.