среда, 24 февраля 2010 г.

Qt и zip архивирование

Архивирование в Qt представлено методами qCompress() qUnCompress(). Они сожмут/разожмут некую последовательность байт, но обо всем остальном (запись в файл, сохранение структуры директорий, запись комментария и проч.) придется позаботиться тебе.
Поэтому для Qt написано много пакетов для работы с архивами.
Я лично предпочитаю The OSDaB Project.
http://code.google.com/p/osdab/source/browse/trunk/OSDaB-Zip
Meet a couple of pure-Qt/C++ classes capable of handling PKZIP 2.0 compatible zip archives.

Чтобы им пользоваться Qt должна быть собрана с поддержкой zlib. Само использование крайне легко, в примерах все описано.
Я же расскажу о неком досадном баге в этом пакете.
Я не буду углубляться в формат zip файла, он довольно сложен, главное вот это
Файл ZIP определяется наличием центрального каталога, который расположен в конце файла. В каталоге хранится список имен записей (файлов или каталогов), которые хранятся в файле ZIP, наряду с другими метаданными о входе и смещении в файле ZIP, указывающими на фактическое расположение сжатых данных.
Проблема в том, что OSDaB считает что центральный каталог начинается начинается с минус 22 байта от конца архива. Т.е. он отступает 22 байта от конца файла архива и ищет там начало секции Центрального Каталога. Секция ЦК начинается с байт PK56. Так вот если он ее там не найдет, то начинается двигаться в начало файла но с шагом кратным 22. Для большинства архивов это работает. По ходу работы пришлось столкнуться с архивами у которых ЦК начинается вовсле не с -22 байта, а с 18 или 21. Поэтому проект пришлось подправить чтобы начинал поиск секции ЦК с 18 и шагал по 1 байту в начало. Это понижает скорость работы архиватора, но по крайней мере нужные мне архивы стали нормально читаться.

Правки, которые пришлось сделать, располагаются в файле  unzip.cpp
Магический параметр UNZIP_EOCD_SIZE установлен равным 18
В функции
UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory()
закоментарена строчка
//offset -= UNZIP_EOCD_SIZE;
чтобы шаг был 1 байт. Кроме того
offset -= 1 /*UNZIP_EOCD_SIZE*/;
И ввел дополнительную проверку
if (offset > length)
   offset = getSULong((
const unsigned char*) buffer1, UNZIP_EOCD_OFF_CDOFF + 4);
Определив функцию getSULong
quint32 UnzipPrivate::getSULong(const unsigned char* data, quint32 offset) const
{
   quint32 res = (quint32) data[offset];
   res |= (((quint32)data[offset+1]) << 8);
  
return res;
}


* This source code was highlighted with Source Code Highlighter.

Комментариев нет:

Отправить комментарий