Russian Qt Forum

Qt => Общие вопросы => Тема начата: 0xFF от Март 01, 2011, 15:08



Название: QZipReader или как распаковать zip
Отправлено: 0xFF от Март 01, 2011, 15:08
Доброго времени суток
Проблема в следующем: нужно распаковать файл MS Office (.docx)
Версия Qt 4.7.0
Из стандартного для этого нашел QZipReader, но что-то он вообще не хотит работать:
Код
C++ (Qt)
QZipReader zip_reader(QLatin1String("doc.docx"));
if (zip_reader.exists()) {
   zip_reader.extractAll("extr");
   zip_reader.close();
}
Пробывал писать вместо extr путь к папке.
Такой код показывает файлы в архиве:
Код
C++ (Qt)
foreach (QZipReader::FileInfo info, zip_reader.fileInfoList()) {
   qDebug() <<  info.filePath;
}
видимо они все же должны как-то извлекаться, раз он их видит. Пробывал с обычными архивами - та же песня, файлы показывает, но ничего не извлекает =(

Что я делаю не так? Может кто-нибудь выложит рабочий проектик где этот QZipReader будет работать, он ведь не документирован, информация кусочна, может я что-либо не так подключил.


Название: Re: QZipReader или как распаковать zip
Отправлено: victor_yacovlev от Март 01, 2011, 19:53
Попробуйте QaZip (http://quazip.sourceforge.net/ (http://quazip.sourceforge.net/)). Документация вполне полная, работоспособность проверена.


Название: Re: QZipReader или как распаковать zip
Отправлено: 0xFF от Март 01, 2011, 23:17
Спасибо
Библиотеку эту собрал, вроде работает, но вопрос остался открытым, все же хотел бы разузнать нерабочесть данного класса в моих руках)


Название: Re: QZipReader или как распаковать zip
Отправлено: voral от Март 02, 2011, 01:47
Странно. Только сегодня фокал под свои нужды это дело все работает (с docx не побовал) вот прямо рабочий код
Код:
#include <qvzip.h>
int main(int argc, char *argv[])
{
        if  (argc<2) return 1;
        QVZipReader zip(argv[1]); // имя файла архива
        zip.extractAll(argv[2]); // куда извлекать
        zip.close();
        return 0;
}

qvzip.h - это Qtшный qzip.cpp
QVZipReader - это Qtшный QZipReader

в данном случае я только переименовал. Это все работает. Хотя одно но я qzip.cpp qzipwriter.h qzipreader.h взял из сырцов qutim (просто под руку попались) - но, думаю, они там Qt-шные.

Кстати, там (в этих файлах) все понятно, так что навставляете туда qDebug`ов или дебагером пройдитесь.

ЗЫ Не знаю кто там пермишены открутил (может qutim-овцы постарались, а может Qt шники не доделали). Но там это недолго поправить. У меня теперь линуксовые права храняться и восстанавливаются.

ЗЗЫ В винде тоже работало

Если поможет - присылайте какой либо файл в формате docx - попробую...


Название: Re: QZipReader или как распаковать zip
Отправлено: 0xFF от Март 02, 2011, 10:57
Подключил попавшиеся вам под руку файлы из qutim. Результат оказался тот же, я поковырялся в qzip.cpp, вообщем незнаю правильно это или нет, но все файлы в архиве определяются как Symlink.
Я закомментил в функции extractAll следующее:
Код
C++ (Qt)
foreach (FileInfo fi, allFiles) {
const QString absPath = destinationDir + QDir::separator() + fi.filePath;
//if (fi.isFile) {
QFile f(absPath);
if (!f.open(QIODevice::WriteOnly))
return false;
f.write(fileData(fi.filePath));
f.setPermissions(fi.permissions);
f.close();
//}
}
и о чудо, файлы разархивировались, но опять трабл, разархивируются только файлы в корне архива, т.е. архивированные папки идут лесом. Такое ощущение что проблемы в ОС (Win7), еще попилю код, надеюсь все же вытащу папки)


Название: Re: QZipReader или как распаковать zip
Отправлено: voral от Март 02, 2011, 13:36
Подключил попавшиеся вам под руку файлы из qutim. Результат оказался тот же, я поковырялся в qzip.cpp, вообщем незнаю правильно это или нет, но все файлы в архиве определяются как Symlink.
и о чудо, файлы разархивировались, но опять трабл, разархивируются только файлы в корне архива, т.е. архивированные папки идут лесом. Такое ощущение что проблемы в ОС (Win7), еще попилю код, надеюсь все же вытащу папки)
Видимо я наврал (память подвела). Под линем не разархивируется сходу. И "обычный" zip архив тоже. Возможно есть отличия по формату со стандартным zip. (http://en.wikipedia.org/wiki/ZIP_%28file_format%29) можно сравнить с кодом.
Возможно docx так же имеет дополнительные поля которые не умеет этот класс обрабатывать. (судя по "внешнему виду" кода он писался на коленке и "лишь бы работало то что нужно в конкретной задаче").
Кстати, в qzip.cpp есть следующее:
Код:
#if defined(Q_OS_WIN)
....
#  define S_ISDIR(x) ((x) & 0040000) > 0
#  define S_ISREG(x) ((x) & 0170000) == S_IFREG
#  define S_IFLNK 020000
#  define S_ISLNK(x) ((x) & S_IFLNK) > 0
...
Может эта байда "пееопределяет" значения для стандартного зипа?


Название: Re: QZipReader или как распаковать zip
Отправлено: crackedmind от Март 02, 2011, 14:03
Похоже все проблемы вот в этой функции
Код
C++ (Qt)
void QZipPrivate::fillFileInfo(int index, QVZipReader::FileInfo &fileInfo) const
{
   FileHeader header = fileHeaders.at(index);
   fileInfo.filePath = QString::fromLocal8Bit(header.file_name);
   const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF;
   fileInfo.isDir = S_ISDIR(mode);
   fileInfo.isFile = S_ISREG(mode);
   fileInfo.isSymLink = S_ISLNK(mode);
   fileInfo.permissions = modeToPermissions(mode);
   fileInfo.crc32 = readUInt(header.h.crc_32);
   fileInfo.size = readUInt(header.h.uncompressed_size);
   fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
}
 
Т.к. не срабатывает правильно проверка на fi.isFile

Цитировать
Может эта байда "пееопределяет" значения для стандартного зипа?
Эта вся беда вообще из линуксов =)


Название: Re: QZipReader или как распаковать zip
Отправлено: voral от Март 02, 2011, 14:27
Цитировать
Может эта байда "пееопределяет" значения для стандартного зипа?
Эта вся беда вообще из линуксов =)
Хм. Пи компиляции под виндой у меня именно на этот код идут варнинги "переопределено"
Цитировать
In file included from ..\src\main.cpp:6:

..\src\/qvzip.h:59:1: warning: "S_ISDIR" redefined

In file included from ..\..\..\Qt\2010.05\qt\mkspecs\win32-g++/qplatformdefs.h:61,
правда не на все


Название: Re: QZipReader или как распаковать zip
Отправлено: 0xFF от Март 02, 2011, 14:57
Не глядя глубоко, это дело чинится парсингом строки с файлом из архива, строка содержит название директорий, где должен лежать файл, по этим названиям восстанавливаем иерархию и распихиваем файлы, пока обойдусь QuaZIP, но думаю вернусь к доделке этого распихивания)


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июнь 26, 2011, 07:40
Ну что? У получилось у тебя что-нибудь? Я сейчас столкнулся как раз с этой же проблемой :) Дай знать, если получилось. Вообще сейчас  у меня в проекте используется OSDABZip, но там нет такой фичи, как QByteArray QZipReader::fileData(const QString &fileName) const, по крайней мере я не нашел.


Название: Re: QZipReader или как распаковать zip
Отправлено: ритт от Июнь 26, 2011, 10:18
дело в том, что QZipReader понимает только пермижены в схеме Unix, тогда как в данном .docx используется схема пермиженов FAT (а ещё все даты 01.01.1980)
могу добавить поддержку схемы FAT/VFAT/NTFS/HPFS в QZipReader 4.8, если создадите таску на http://bugreports.qt.nokia.com/

з.ы. quazip - отстой


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июнь 26, 2011, 11:35
2Константин: я пытаюсь распаковать обычный зип, зипованый линуксовым зипом.


Название: Re: QZipReader или как распаковать zip
Отправлено: ритт от Июнь 26, 2011, 11:41
у меня работает отлично. правда, я внёс ряд изменений в свою версию...
давайте компилябельный пример с тестовым архивом.


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июнь 26, 2011, 12:27
Вот. Список файлов в архиве выводит в дебаг, файлы из архива не вытаскиваются.
Код:
$ dpkg -s zip | grep -i version
Version: 3.0-3ppa2


Название: Re: QZipReader или как распаковать zip
Отправлено: ритт от Июнь 26, 2011, 15:12
сначала у меня ругалось на экстра-блоки и я было решил, что дело в них, но оказалось, что я просто сломал у себя парсер экстра-блоков...потом ещё вспомнил, что QZipReader  экстра-блоки вообще игнорирует - т.е. дело точно не в них.

заменил в примере
qDebug()<<list.at(i).filePath;
на
qDebug()<<list.at(i).filePath << list.at(i).isDir << list.at(i).isFile << list.at(i).isSymLink << list.at(i).size;
и сразу стало ясно - читайте http://www.prog.org.ru/index.php?topic=16992.msg125304#msg125304


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июнь 26, 2011, 15:51
Гм.. Сделал архив qzipом. Результат:
Код:
"test/" 1 0 0 0 
"test/pict.jpg" 0 1 0 9229
"test/test.odt" 0 1 0 18527
И теперь как таковая распаковка происходит, но в каталог назначения записывается только test/, а в сам test/ ничего не записывается, т.к. да, пермишины.


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июнь 26, 2011, 17:02
В общем подправил немного под свои нужды. В конструкторе class GZipWriterPrivate : public QZipPrivate исправил:
Код
C++ (Qt)
permissions(QFile::ReadOwner | QFile::WriteOwner)
на
Код
C++ (Qt)
permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)
это позволило сохранять нужные(?) права на каталог.
И еще при тестировании под виндой нашел один баг(?) с сепаратором в QZipWriter::addDirectory:
Код
C++ (Qt)
   if (!name.endsWith(QDir::separator()))
       name.append(QDir::separator());
Так у меня не работает. В создаваемом каталоге появляется файл нулевой длины. Под виндой такой архив распаковывается нормально. Под линем при распаковке к каталогу добавляется виндовый "\". Вместо QDir::separator() сделал просто "/".


Название: Re: QZipReader или как распаковать zip
Отправлено: ритт от Июнь 26, 2011, 17:13
действительно, нужно
Код:
name = QDir::fromNativeSeparators(name);
if (!name.endsWith(QLatin1Char('/'))
    name += QLatin1Char('/');
посмотрел у себя - там исправлено...

> permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)
так неправильно. нужно так:
Код:
#ifndef FILE_ATTRIBUTE_READONLY
#  define FILE_ATTRIBUTE_READONLY 0x1
#endif
#ifndef FILE_ATTRIBUTE_DIRECTORY
#  define FILE_ATTRIBUTE_DIRECTORY 0x10
#endif

enum HostOS {
    HostFAT      = 0,
    HostAMIGA    = 1,
    HostVMS      = 2,  // VAX/VMS
    HostUnix     = 3,
    HostVM_CMS   = 4,
    HostAtari    = 5,  // what if it's a minix filesystem? [cjh]
    HostHPFS     = 6,  // filesystem used by OS/2 (and NT 3.x)
    HostMac      = 7,
    HostZ_System = 8,
    HostCPM      = 9,
    HostTOPS20   = 10, // pkzip 2.50 NTFS
    HostNTFS     = 11, // filesystem used by Windows NT
    HostQDOS     = 12, // SMS/QDOS
    HostAcorn    = 13, // Archimedes Acorn RISC OS
    HostVFAT     = 14, // filesystem used by Windows 95, NT
    HostMVS      = 15,
    HostBeOS     = 16, // hybrid POSIX/database filesystem
    HostTandem   = 17,
    HostOS400    = 18,
    HostOSX      = 19
};

void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const
{
    FileHeader header = fileHeaders.at(index);
    fileInfo.filePath = QString::fromLocal8Bit(header.file_name);
    quint32 mode = readUInt(header.h.external_file_attributes);
    const HostOS hostOS = HostOS(readUShort(header.h.version_made) >> 8);
    switch (hostOS) {
    case HostUnix:
        mode = (mode >> 16) & 0xffff;
        if (S_ISDIR(mode))
            fileInfo.isDir = true;
        else if (S_ISREG(mode))
            fileInfo.isFile = true;
        else if (S_ISLNK(mode))
            fileInfo.isSymLink = true;
        fileInfo.permissions = modeToPermissions(mode);
        break;
    case HostFAT:
    case HostNTFS:
    case HostHPFS:
    case HostVFAT:
        fileInfo.permissions |= QFile::ReadOwner | QFile::ReadUser | QFile::ReadGroup | QFile::ReadOther;
        if ((mode & FILE_ATTRIBUTE_READONLY) == 0)
            fileInfo.permissions |= QFile::WriteOwner | QFile::WriteUser | QFile::WriteGroup | QFile::WriteOther;
        if ((mode & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
            fileInfo.isDir = true;
            fileInfo.permissions |= QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther;
        } else {
            fileInfo.isFile = true;
        }
        break;
    default:
        return; // we don't support anything else
    }
    fileInfo.crc32 = readUInt(header.h.crc_32);
    fileInfo.size = readUInt(header.h.uncompressed_size);
    fileInfo.lastModified = readMSDosDate(header.h.last_mod_file);
}


Название: Re: QZipReader или как распаковать zip
Отправлено: ритт от Июнь 26, 2011, 17:15
кстати, из
struct Q_GUI_EXPORT FileInfo
нужно убрать Q_GUI_EXPORT (у Вас может быть другой другой макрос - не суть, его нужно убрать)


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июнь 26, 2011, 18:06
Так у меня не работает. Если распаковывать архив с правильными пермишинами, то создается только структура каталогов, файлы не извлекаются. Если попытаться запаковать, то пермишины становятся неправильными (у каталогов и файлов только rw, а х нету).


Название: Re: QZipReader или как распаковать zip
Отправлено: ритт от Июнь 26, 2011, 18:29
код выше относится только к чтению - к упаковке отношения не имеет.
а что вывод `qDebug()<<list.at(i).filePath << list.at(i).isDir << list.at(i).isFile << list.at(i).isSymLink << list.at(i).size;` кажет?


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июнь 26, 2011, 19:02
код выше относится только к чтению - к упаковке отношения не имеет.
а что вывод `qDebug()<<list.at(i).filePath << list.at(i).isDir << list.at(i).isFile << list.at(i).isSymLink << list.at(i).size;` кажет?
Я не про Ваш код, а про свою правку конструктора:
> permissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner)
так неправильно. нужно так:
...
Если из конструктора убрать QFile::ExeOwner, то запаковывается неправильно соответственно распаковать нельзя.
Если распаковываю правильный архив то получаю:
Код:
"tests/" 1 1 0 0 
"tests/img/" 1 1 0 0
"tests/tests.xml" 0 1 0 7512
Первые две записи - каталоги, последняя - файл, но при распаковке создаются только каталоги:
Код:
$ ls  -R ~/Desktop/t/
/home/nortt/Desktop/t/:
tests

/home/nortt/Desktop/t/tests:
img

/home/nortt/Desktop/t/tests/img:


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июнь 27, 2011, 12:31
В общем взял QZip из исходников Qt 4.7.3. Все заработало. До этого были исходники из odf-редактора с qt-apps.org, просто сразу я не подозревал, что это есть в исходниках Qt. Я думал это велосипед odf-редактора и пытался прикрутить его к своему проекту, убив два дня на это :)
P.S. 2Константин, спасибо за помощь.


Название: Re: QZipReader или как распаковать zip
Отправлено: ритт от Июнь 27, 2011, 13:20
пожалуйста. а код из поста http://www.prog.org.ru/index.php?topic=16992.msg125330#msg125330 всё же советую прикрутить...


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июль 03, 2011, 10:20
При сборке проекта под виндой возникли проблемы:
Цитировать
c:\QtSDK\Desktop\Qt\4.7.3\mingw\lib/libQtGui4.a(d000607.o):(.text+0x0): multiple definition of `QZipReader::QZipReader(QString const&, QFlags<QIODevice::OpenModeFlag>)'
./release\qzip.o:qzip.cpp:(.text+0x788): first defined here
Под линем собирается без ошибок. Что нужно сделать?


Название: Re: QZipReader или как распаковать zip
Отправлено: NortT от Июль 03, 2011, 13:22
А все придумал, заюзал namespace.


Название: Re: QZipReader или как распаковать zip
Отправлено: ритт от Май 16, 2012, 11:05
патч, упоминавшийся ранее, залит в 5.0. чуть позже ещё залью.
поддержку экстра-блоков пока отложу до лучших времён...