Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Общие вопросы _ ZIP архив создать программно как ?

Автор: flankerr 9.10.2009, 10:13

Есть некий набор данных который создаёт программа. Надо их опяттаки программно сжать в ZIP и сохранить. Разумеется хочется чтоб была возможность следить за процеесом для возможности информировать сколько осталось время до конца сжатия. Всякие вызовы сторонних EXE отметаються сразу. Ну и кросплатформенность ни кто не отменял.
Может кто в курсе как это организовать ?

Автор: Litkevich Yuriy 9.10.2009, 11:01

помоему на прогорге это обширно обсуждалось. Можно http://doc.crossplatform.ru/qt/4.5.0/qbytearray.html#qCompress

Вот чего накопал по этой теме:
zlib (та что с Qt) - только методы сжатия данных.
http://quazip.sourceforge.net/ - не поддерживает подкаталоги
http://www.info-zip.org/ - нет информации (надо разбиратся)
http://zziplib.sourceforge.net/ - нет информации (надо разбиратся)


Могут оказатся полезными:
http://qtnode.net/wiki?title=Self-Extracting_Installer
http://osdab.42cows.org/?mode=advanced - какие-то фрагменты кода на C++/Qt, в том числе http://osdab.42cows.org/snippets/zip.php?mode=advanced

Автор: sploid 9.10.2009, 12:11

у тролей есть zlib как 3rdparty

src/3rdparty/zlib/

http://qt.nokia.com/doc/4.5/3rdparty.html#data-compression-library-version-1-2-3

Автор: flankerr 9.10.2009, 12:34

Если как сжать набор байт стало относительно понятно.
Но вот как сделать прогесбар пока не ясно. Вызвать тот же qCompress и тупо ждать неизвестно сколько пока он сжимает как то совсем мимо цели Т.к. процесс сжатия может занять достаточно длительное время то прогрессбар просто обязан быть.
Да и что делать для сжать папки с вложенными папками и файлами тоже не ясно.

Городить огород с собственным форматом выходного файла нельзя т.к. необходимо чтобы результат сжатия можно было распаковатть классическим ZIP-ом.

Автор: kwisp 9.10.2009, 13:53

flankerr,
что ни одно из предоложенных решений Юрием не предоставляет возможности сжимать вложенные паки?
и никак не прикрутишь прогрессБар?

Автор: Litkevich Yuriy 9.10.2009, 15:52

Цитата(kwisp @ 9.10.2009, 17:53) *
что ни одно из предоложенных решений Юрием не предоставляет возможности сжимать вложенные паки?
и никак не прикрутишь прогрессБар?
возможно, ни одно из них не предоставляет обратной связи.

Собсна это болезнь многих писателей консольных программ/библиотек.

Автор: flankerr 9.10.2009, 16:20

kwisp,
QuaZIP
Zlib
qCompress
жмут поток т.е. папку с вложением подсунуть нельзя. И как правильно сказал Litkevich Yuriy, нет обратной связи.

Автор: Litkevich Yuriy 9.10.2009, 16:36

flankerr, видимо у тебя один выход - заниматся раскопками ОпенСорсных проектов типа http://7-zip.org.ua/ru/help/supported_formats.html#zip...

Автор: SABROG 9.10.2009, 17:11

Цитата(Litkevich Yuriy @ 9.10.2009, 17:36) *
flankerr, видимо у тебя один выход - заниматся раскопками ОпенСорсных проектов типа http://7-zip.org.ua/ru/help/supported_formats.html#zip...


Бессмысленно. Мои копания привели к тому, что разработчики предоставляют библиотеку только для создания 7z архивов, ни zip, ни rar и т.п.

Автор: Litkevich Yuriy 9.10.2009, 18:28

Однако исодники у них открыты, или я ошибаюсь?

Автор: SABROG 9.10.2009, 23:39

Цитата(Litkevich Yuriy @ 9.10.2009, 19:28) *
Однако исодники у них открыты, или я ошибаюсь?

Открыты, только тогда придеться брать исходники p7zip (порт под linux). Там вроде бы какраз есть директория zip с исходниками. Думаю там точно такая же канитель будет с пониманием ABI, как и в InfoZip.

Автор: BRE 11.10.2009, 12:25

Можно посмотреть решение в KDE. Там правда все сделано глобально, т.е. есть целая иерархия классов начиная с KArchive. У него есть наследник KZip.
Или посмотреть на библиотеку libzip. Она сишная, но можно самому написать враппер для C++ со всем необходимым функционалом.
Также вроде есть готовый враппер libzipios++.

Автор: Litkevich Yuriy 11.10.2009, 12:33

в KDE в большенстве случаев индикатор выполнения болтается туда-сюда.

Автор: BRE 11.10.2009, 12:46

Цитата(Litkevich Yuriy @ 11.10.2009, 13:33) *
в KDE в большенстве случаев индикатор выполнения болтается туда-сюда.

Если писать свой враппер, то это можно сделать как надо:
* Открыть архив
* Найти по имени файла его индекс
* Получить информацию о файле по индексу (необходим размер распакованных данных)
* В цикле читаем данные файла из архива кусками и посылаем сигнал о прогрессе.

Автор: kuzulis 12.10.2009, 8:13

А почему автору нельзя использовать сторонние архиваторы?

Проще, ИМХО, через QProcess их вызывать и в опциях командной строки подсовывать нужные каталоги для сжатия...

Для этого в самой программе автора определять - какие из архиваторов находятся в системе... НЕ ? :)

Автор: kwisp 12.10.2009, 8:23

kuzulis,
первый пост об этом говорит.

Автор: filinGUI 12.10.2009, 14:11

flankerr

Архивирование с вложенными каталогами и отображением прогресса можно сделать, используя Info-ZIP. Архив получается вполне стандартный и распакуется везде. Надо только собрать zip и unzip как dll и написать callback-функцию для отображения прогресса.

Автор: SABROG 12.10.2009, 14:30

Цитата(filinGUI @ 12.10.2009, 15:11) *
flankerr

Архивирование с вложенными каталогами и отображением прогресса можно сделать, используя Info-ZIP. Архив получается вполне стандартный и распакуется везде. Надо только собрать zip и unzip как dll и написать callback-функцию для отображения прогресса.


Найти бы еще пример как это делать. Мои копания примеров этой утилиты привели только к одному заключению - функций типа createZip()/Unzip() нет. Т.е. нужно делать какие-то подготовительные телодвижения, логика которых не становится понятной глядя на код.

Автор: filinGUI 12.10.2009, 15:11

Цитата(SABROG @ 12.10.2009, 15:30) *
Найти бы еще пример как это делать. Мои копания примеров этой утилиты привели только к одному заключению - функций типа createZip()/Unzip() нет. Т.е. нужно делать какие-то подготовительные телодвижения, логика которых не становится понятной глядя на код.


Таких простых функций действительно нет, для упаковки используется последовательность вызовов ZpInit / ZpSetOptions / ZpArchive, для распаковки вызывается Wiz_SingleEntryUnzip. Для своего проекта я написал функцию SimpleUnzip (Qt3, библиотека Unzip.dll загружается динамически)
Раскрывающийся текст
//---------------------------------------------------------------------------
//  Служебные функции для SimpleUnzip
//---------------------------------------------------------------------------
int WINAPI DummyPrint(LPSTR, unsigned long) {return 0;}
int WINAPI DummyReplace(LPSTR) {return 0;}
int WINAPI DummyPassword(LPSTR, int, LPCSTR, LPCSTR) {return 0;}
void WINAPI DummyMessage(unsigned long, unsigned long, unsigned,
    unsigned, unsigned, unsigned, unsigned, unsigned,
    char, LPSTR, LPSTR, unsigned long, char) {}

//---------------------------------------------------------------------------
//  Распаковывает zip-архив a_zip в каталог a_dir, заменяя существующие файлы
//---------------------------------------------------------------------------
typedef int (WINAPI * FSINGLEENTRYUNZIP)( int, char **, int, char **,
                                          LPDCL, LPUSERFUNCTIONS );
bool SimpleUnzip( const QString& a_zip, const QString& a_dir )
{
    const QString noZip = ru( "Обработка архивов zip невозможна." );
    const QString errCap = ru("Ошибка");
    const TCHAR* UNZ_DLL_NAME = TEXT("unzip32.dll");
    TCHAR szFullPath[_MAX_PATH];
    TCHAR* ptr;
    if ( SearchPathW( 0, UNZ_DLL_NAME, 0,
                      sizeof(szFullPath) / sizeof(szFullPath[ 0 ]),
                      szFullPath, &ptr ) == 0 )
    {
        mbE( errCap, ru("Не найдена библиотека unzip32.dll.\n" + noZip ) );
        return false;
    }

    HMODULE hUnzipDll = LoadLibraryW( UNZ_DLL_NAME );
    if ( hUnzipDll == 0 )
    {
        mbE( errCap, ru("Не удалось загрузить библиотеку unzip32.dll.\n" + noZip ) );
        return false;
    }

    FSINGLEENTRYUNZIP pWiz_SingleEntryUnzip = (FSINGLEENTRYUNZIP)GetProcAddress(
        hUnzipDll, "Wiz_SingleEntryUnzip" );
    if ( pWiz_SingleEntryUnzip == 0 )
    {
        FreeLibrary( hUnzipDll );
        mbE( errCap, ru("Неправильная библиотека unzip32.dll.\n" + noZip ) );
        return false;
    }

    LPDCL lpDCL = new DCL;
    LPUSERFUNCTIONS lpUserFunctions = new USERFUNCTIONS;

    lpUserFunctions->password = DummyPassword;
    lpUserFunctions->print = DummyPrint;
    lpUserFunctions->replace = DummyReplace;
    lpUserFunctions->SendApplicationMessage = DummyMessage;
    lpUserFunctions->sound = 0;
    lpUserFunctions->ServCallBk = 0;

    //set up the flags to be passed into the dll.
    lpDCL->ExtractOnlyNewer = 0;  // Do not extract only newer
    lpDCL->SpaceToUnderscore = 0;
    lpDCL->PromptToOverwrite = 0; // "Overwrite all" selected, no query mode
    lpDCL->fQuiet = 2; // 0 = all messages, 1 = fewer messages, 2 = no messages
    lpDCL->ncflag = 0; // Write to stdout if true
    lpDCL->ntflag = 0; // Do not test zip file
    lpDCL->nvflag = 0; // Do not give a verbose listing
    lpDCL->nfflag = 0; // Do not freshen existing files only
    lpDCL->nzflag = 0; // display a zip file comment if true
    lpDCL->ndflag = 1; // Recreate directories != 0, skip "../" if < 2
    lpDCL->noflag = 1; // Over-write all files
    lpDCL->naflag = 0; // Do not convert CR to CRLF
    lpDCL->nZIflag = 0;// Do not get ZipInfo
    lpDCL->C_flag = 1; // Do not be case insensitive
    lpDCL->fPrivilege = 0;  // 1 => restore ACLs in user mode,
                        // 2 => try to use privileges for restoring ACLs

    QCString zip = a_zip.local8Bit();
    QCString dir = a_dir.local8Bit();
    lpDCL->lpszZipFN = const_cast<CHAR*>(static_cast<const char*>(zip));
    if ( a_dir.isEmpty() )
        lpDCL->lpszExtractDir = 0;
    else
        lpDCL->lpszExtractDir = const_cast<CHAR*>(static_cast<const char*>(dir));

//    qDebug( "zip=%s, dir=%s", lpDCL->lpszZipFN, lpDCL->lpszExtractDir );
    int res = pWiz_SingleEntryUnzip( 0, 0, 0, 0, lpDCL, lpUserFunctions );
//    qDebug( "unzip retcode=%d", res );
    bool rc = res == 0;

    delete lpDCL;
    delete lpUserFunctions;
    FreeLibrary( hUnzipDll );

    return rc;
}


Могу дать ещё пример SimpleZip для упаковки, но только на Delphi

Автор: SABROG 12.10.2009, 15:19

Плохо это решение тем, что оно не переносимо, ибо этот Wizard dll API зависим в то время как внутренние вызовы самой библиотеки InfoZip переносимы. Т.е., чтобы понять как это работает и правильно написать код, надо сначала переписать Wizard dll на обычном C++ с использованием STL или обычном Си. Потом уже этот код использовать в Qt.

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)