Описание формата ZIP файла. Часть 1

ZIP файл состоит из трех областей:

  • сжатые/несжатые данные, (последовательность структур типа LocalFileHeader, сами данные и необязательных DataDescriptor)
  • центральный каталог (последовательность структур CentralDirectoryFileHeader)
  • описание центрального каталога (End of central directory record (EOCD))

С начала файла идет набор из LocalFileHeader, непосредственно данные и (необязательно) структура Data descriptor. Затем структуры типа CentralDirectoryFileHeader для каждого файла и папки в ZIP архиве и завершает все это структура End of central directory record.

Local File Header

Используется для описания метаданных файла (имя файла, контрольная сумма, время и дата модификации, сжатый/несжатый размер). Как правило сразу после этой структуры следует содержимое файла.

LocalFileHeader

struct LocalFileHeader
{
    // Обязательная сигнатура, равна 0x04034b50
    uint32_t signature;
    // Минимальная версия для распаковки
    uint16_t versionToExtract;
    // Битовый флаг
    uint16_t generalPurposeBitFlag;
    // Метод сжатия (0 - без сжатия, 8 - deflate)
    uint16_t compressionMethod;
    // Время модификации файла
    uint16_t modificationTime;
    // Дата модификации файла
    uint16_t modificationDate;
    // Контрольная сумма
    uint32_t crc32;
    // Сжатый размер
    uint32_t compressedSize;
    // Несжатый размер
    uint32_t uncompressedSize;
    // Длина название файла
    uint16_t filenameLength;
    // Длина поля с дополнительными данными
    uint16_t extraFieldLength;
    // Название файла (размером filenameLength)
    uint8_t *filename;
    // Дополнительные данные (размером extraFieldLength)
    uint8_t *extraField;
};

Сразу после этой структуры идут данные размером compressedSize при использовании сжатия или размером uncompressedSize в противном случае.

Иногда бывает невозможно вычислить данные на момент записи LocalFileHeader, тогда в crc32, compressedSize и uncompressedSize записываются нули, третий бит в generalPurposeBitFlag ставится в единицу, а после LocalFileHeader добавляется структура типа DataDescriptor.

Data descriptor

Если по какой-то причине содержимое файла невозможно создать одновременно с заголовком типа LocalFileHeader, то сразу после него следует структура DataDescriptor, где идет находится дополнение метаданных для LocalFileHeader (контрольная сумма, сжатый/несжатый размер).

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

DataDescriptor

struct DataDescriptor
{
    // Необязательная сигнатура, равна 0x08074b50
    uint32_t signature;
    // Контрольная сумма
    uint32_t crc32;
    // Сжатый размер
    uint32_t compressedSize;
    // Несжатый размер
    uint32_t uncompressedSize;
};

Central directory file header

Расширенное описание метаданных файла. Содержит дополненную версию LocalFileHeader (добавляются поля номер диска, файловые атрибуты, смещение до LocalFileHeader от начала ZIP файла).

CenterDirectoryFileHeader

struct CentralDirectoryFileHeader
{
    // Обязательная сигнатура, равна 0x02014b50 
    uint32_t signature;
    // Версия для создания
    uint16_t versionMadeBy;
    // Минимальная версия для распаковки
    uint16_t versionToExtract;
    // Битовый флаг
    uint16_t generalPurposeBitFlag;
    // Метод сжатия (0 - без сжатия, 8 - deflate)
    uint16_t compressionMethod;
    // Время модификации файла
    uint16_t modificationTime;
    // Дата модификации файла
    uint16_t modificationDate;
    // Контрольная сумма
    uint32_t crc32;
    // Сжатый размер
    uint32_t compressedSize;
    // Несжатый размер
    uint32_t uncompressedSize;
    // Длина название файла
    uint16_t filenameLength;
    // Длина поля с дополнительными данными
    uint16_t extraFieldLength;
    // Длина комментариев к файлу
    uint16_t fileCommentLength;
    // Номер диска
    uint16_t diskNumber;
    // Внутренние аттрибуты файла
    uint16_t internalFileAttributes;
    // Внешние аттрибуты файла
    uint32_t externalFileAttributes;
    // Смещение до структуры LocalFileHeader
    uint32_t localFileHeaderOffset;
    // Имя файла (длиной filenameLength)
    uint8_t *filename;
    // Дополнительные данные (длиной extraFieldLength)
    uint8_t *extraField;
    // Комментарий к файла (длиной fileCommentLength)
    uint8_t *fileComment;
};

End of central directory record (EOCD)

Эта структура записывается в конце файла. Содержит следующие поля: номер текущего диска, количество записей CentralDirectoryFileHeader в текущем диске, общее количество записей CentralDirectoryFileHeader.

EOCD

struct EOCD
{
    // Обязательная сигнатура, равна 0x06054b50
    uint32_t signature;
    // Номер диска
    uint16_t diskNumber;
    // Номер диска, где находится начало Central Directory
    uint16_t startDiskNumber;
    // Количество записей в Central Directory в текущем диске
    uint16_t numberCentralDirectoryRecord;
    // Всего записей в Central Directory
    uint16_t totalCentralDirectoryRecord;
    // Размер Central Directory
    uint32_t sizeOfCentralDirectory;
    // Смещение Central Directory
    uint32_t centralDirectoryOffset;
    // Длина комментария
    uint16_t commentLength;
    // Комментарий (длиной commentLength)
    uint8_t *comment;
};

Папки в ZIP файле представлены двумя структурами LocalFileHeader и CentralDirectoryFileHeader с нулевым размером и контрольной суммой. Название папки заканчивается слешем «/».

8 комментариев
  1. написал(а) Аноним (25 февраля 2016, 11:08)

    Спасибо, очень полезно!

    1. написал(а) eJ (25 февраля 2016, 12:33)

      Всегда пожалуйста!

  2. написал(а) Vadim (22 июня 2020, 11:38)

    Кстати, если архивируется ОДИН файл размером <4Гб, то наличие Central directory необязательно.
    Достаточно Local File Header либо Local File Header+Data descriptor.
    Проверено.

  3. написал(а) Vadim (22 июня 2020, 14:30)

    …проверено.
    Но работает такой вариант только в Far Manager 3 !!!
    К сожалению, Winrar и 7zip на такие файлы сильно ругаются.

    Та же история и с архивами из одиночного файла, размер которого >4Gb.
    Если использовать ТОЛЬКО Local File Header + ZIP64ExtendInformation в ExtraFieldRecord,
    то Far3 так же прекрасно открывает такой файл, а винрар и 7зип пасуют…

    Так что для всеобщей совместимости придётся всё-таки добавлять в архив Central directory :(

    1. написал(а) eJ (22 июня 2020, 14:48)

      Я писал в статье про чтение ZIP файла что можно читать только Local File Header без EOCD. Просто FarManager умеет читать сломанные архивы без секции EOCD. Собственно, восстановление ZIP файла не читает EOCD, а сразу бегает по Local File Header.

  4. написал(а) Vadim (22 июня 2020, 17:58)

    «Если по какой-то причине содержимое файла невозможно создать одновременно с заголовком типа LocalFileHeader, то сразу после него следует структура DataDescriptor, где идет находится дополнение метаданных для LocalFileHeader (контрольная сумма, сжатый/несжатый размер).»

    Тут что-то немного напутано.
    Если архиватор не имеет возможности сразу заполнить все поля LocalFileHeader [не известны размер исходного файла и/или размер данных после сжатия и/или чексумма исходного файла],

    то он просто забивает соответствующие поля в заголовке LocalFileHeader нулями и взводит в том же заголовке флаг [бит 3, т.е. получается 0x0008].

    И уже после того как все данные пожаты и стали известны и CRC, и длины файла до и после сжатия, к концу сжатых данных добавляется DataDescriptor, из которого потом при разархивировании и берутся нужные сведения.

    Проверял: в Far3 эта схема работает :)

  5. написал(а) Figoro (6 ноября 2020, 21:38)

    Ну не понятно же :)
    — А если серьезно, ну вот есть заголовок «Local File Header», который начинается с 0 байта самого ZIP-файла, по его размеру можно определить его окончание. НО, что начинается после окончания заголовка «Local File Header» ??
    Сами сжатые данные ? Но тогда, где и каким образом располагается заголовок/заголовки «Central directory file header» ? И как из расположение учитывать при считывании самих сжатых данных ?
    -Так же не понятно, где начинается заголовок «End of central directory record».

    В общем к сожалению статья больше написана просто для галочки, и если честно почти ничего не проясняет.

    1. написал(а) eJ (6 ноября 2020, 23:36)

      «End of central directory record» находится в самом конце файла. Его точное расположение: размер_архива — размер_структуры_EOCD. В этой же структуре содержится смещение для чтения первого «Central directory file header» в поле «centralDirectoryOffset».

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Блог Евгения Жирнова