Zipping a folder/file using Qt
Asked Answered
W

4

17

I would like to know if there is any class in Qt which can zip a folder or file. I used QProcess to compress, it got compressed but I am unable to uncompress it using a normal zip tool. Can anyone let me know how can we compress a folder/file using Qt api classes?

Wolfhound answered 8/4, 2010 at 7:3 Comment(0)
J
36

A few years ago I had such a issue, and here is my solution:

1). get QuaZip (here is the link text)

2). include quazip sources to your project file

Headers:

HEADERS += src/quazip/crypt.h \
           src/quazip/ioapi.h \
           src/quazip/quazip.h \
           src/quazip/quazipfile.h \
           src/quazip/quazipfileinfo.h \
           src/quazip/quazipnewinfo.h \
           src/quazip/unzip.h \
           src/quazip/zip.h \
           ...

Sources:

SOURCES += src/quazip/ioapi.c \
           src/quazip/quazip.cpp \
           src/quazip/quazipfile.cpp \
           src/quazip/quazipnewinfo.cpp \
           src/quazip/unzip.c \
           src/quazip/zip.c
           ...

3). add headers

#include "quazip/quazip.h"
#include "quazip/quazipfile.h"

4). use extract function:

static bool extract(const QString & filePath, const QString & extDirPath, const QString & singleFileName = QString("")) {

QuaZip zip(filePath);

if (!zip.open(QuaZip::mdUnzip)) {
    qWarning("testRead(): zip.open(): %d", zip.getZipError());
    return false;
}

zip.setFileNameCodec("IBM866");

qWarning("%d entries\n", zip.getEntriesCount());
qWarning("Global comment: %s\n", zip.getComment().toLocal8Bit().constData());

QuaZipFileInfo info;

QuaZipFile file(&zip);

QFile out;
QString name;
char c;
for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) {

    if (!zip.getCurrentFileInfo(&info)) {
        qWarning("testRead(): getCurrentFileInfo(): %d\n", zip.getZipError());
        return false;
    }

    if (!singleFileName.isEmpty())
        if (!info.name.contains(singleFileName))
            continue;

    if (!file.open(QIODevice::ReadOnly)) {
        qWarning("testRead(): file.open(): %d", file.getZipError());
        return false;
    }

    name = QString("%1/%2").arg(extDirPath).arg(file.getActualFileName());

    if (file.getZipError() != UNZ_OK) {
        qWarning("testRead(): file.getFileName(): %d", file.getZipError());
        return false;
    }

    //out.setFileName("out/" + name);
    out.setFileName(name);

    // this will fail if "name" contains subdirectories, but we don't mind that
    out.open(QIODevice::WriteOnly);
    // Slow like hell (on GNU/Linux at least), but it is not my fault.
    // Not ZIP/UNZIP package's fault either.
    // The slowest thing here is out.putChar(c).
    while (file.getChar(&c)) out.putChar(c);

    out.close();

    if (file.getZipError() != UNZ_OK) {
        qWarning("testRead(): file.getFileName(): %d", file.getZipError());
        return false;
    }

    if (!file.atEnd()) {
        qWarning("testRead(): read all but not EOF");
        return false;
    }

    file.close();

    if (file.getZipError() != UNZ_OK) {
        qWarning("testRead(): file.close(): %d", file.getZipError());
        return false;
    }
}

zip.close();

if (zip.getZipError() != UNZ_OK) {
    qWarning("testRead(): zip.close(): %d", zip.getZipError());
    return false;
}

return true;
}

and archive function:

static bool archive(const QString & filePath, const QDir & dir, const QString & comment = QString("")) {

QuaZip zip(filePath);
zip.setFileNameCodec("IBM866");

if (!zip.open(QuaZip::mdCreate)) {
    myMessageOutput(true, QtDebugMsg, QString("testCreate(): zip.open(): %1").arg(zip.getZipError()));
    return false;
}

if (!dir.exists()) {
    myMessageOutput(true, QtDebugMsg, QString("dir.exists(%1)=FALSE").arg(dir.absolutePath()));
    return false;
}

QFile inFile;

// Получаем список файлов и папок рекурсивно
QStringList sl;
recurseAddDir(dir, sl);

// Создаем массив состоящий из QFileInfo объектов
QFileInfoList files;
foreach (QString fn, sl) files << QFileInfo(fn);

QuaZipFile outFile(&zip);

char c;
foreach(QFileInfo fileInfo, files) {

    if (!fileInfo.isFile())
        continue;

    // Если файл в поддиректории, то добавляем имя этой поддиректории к именам файлов
    // например: fileInfo.filePath() = "D:\Work\Sources\SAGO\svn\sago\Release\tmp_DOCSWIN\Folder\123.opn"
    // тогда после удаления части строки fileNameWithSubFolders будет равен "Folder\123.opn" и т.д.
    QString fileNameWithRelativePath = fileInfo.filePath().remove(0, dir.absolutePath().length() + 1);

    inFile.setFileName(fileInfo.filePath());

    if (!inFile.open(QIODevice::ReadOnly)) {
        myMessageOutput(true, QtDebugMsg, QString("testCreate(): inFile.open(): %1").arg(inFile.errorString().toLocal8Bit().constData()));
        return false;
    }

    if (!outFile.open(QIODevice::WriteOnly, QuaZipNewInfo(fileNameWithRelativePath, fileInfo.filePath()))) {
        myMessageOutput(true, QtDebugMsg, QString("testCreate(): outFile.open(): %1").arg(outFile.getZipError()));
        return false;
    }

    while (inFile.getChar(&c) && outFile.putChar(c));

    if (outFile.getZipError() != UNZ_OK) {
        myMessageOutput(true, QtDebugMsg, QString("testCreate(): outFile.putChar(): %1").arg(outFile.getZipError()));
        return false;
    }

    outFile.close();

    if (outFile.getZipError() != UNZ_OK) {
        myMessageOutput(true, QtDebugMsg, QString("testCreate(): outFile.close(): %1").arg(outFile.getZipError()));
        return false;
    }

    inFile.close();
}

// + комментарий
if (!comment.isEmpty())
    zip.setComment(comment);

zip.close();

if (zip.getZipError() != 0) {
    myMessageOutput(true, QtDebugMsg, QString("testCreate(): zip.close(): %1").arg(zip.getZipError()));
    return false;
}

return true;
}

5). enjoy ;)

UPDATE: for CapDroid.

/* Рекурсивно ищет все файлы в директории \a и добавляет в список \b */
static void recurseAddDir(QDir d, QStringList & list) {

    QStringList qsl = d.entryList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files);

    foreach (QString file, qsl) {

        QFileInfo finfo(QString("%1/%2").arg(d.path()).arg(file));

        if (finfo.isSymLink())
            return;

        if (finfo.isDir()) {

            QString dirname = finfo.fileName();
            QDir sd(finfo.filePath());

            recurseAddDir(sd, list);

        } else
            list << QDir::toNativeSeparators(finfo.filePath());
    }
}
Jabber answered 8/4, 2010 at 9:3 Comment(4)
QuaZip is LGPL, so you must either have your main program in a license compatible with LGPL (LGPL or GPL for example) or distribute the QuaZip library as a separate dynamic library file and not include any of its code statically in your program.Acey
If you simply skip folders, QuaZip won't be able to list them when you try extracting your zip. You need to create an empty file with name ending with "/". See https://mcmap.net/q/743203/-how-can-i-zip-a-directory-folder-with-quazipLuganda
Consider to use include(thirdParty/quazip/quazip.pri) and LIBS += -lz to import QuaZip to your Qt project.Rasia
In the recurseAddDir() you 'return' if a file isSymLink() is true. Wouldn't you want to 'continue' instead? The first SymLink you run into will make you skip all other files in the directory.Defeatism
S
5

I don't think you can. As far as I know qcompress and quncompress only provide compressing of streams etc. This means they will not create the headers needed for a real zipfile (which also say which files are in the file).

However there is an open source Qt library called QuaZip which uses zlib (comes with qt) and provides just that.

Stav answered 8/4, 2010 at 7:11 Comment(0)
K
5

I'm using Qt 5.12 version.

In pro file, add "QT += gui-private".

In the cpp file, example:

#include "QtGui/private/qzipreader_p.h"
#include "QtGui/private/qzipwriter_p.h"

......


QFileInfoList list = getFileList(zipPath);//zipPath
QZipWriter writer(zipFile);
if(list.size()<=0){
    qDebug()<<"there is no file to zip, please check it";
    return false;
}
//
for(int i=0;i != list.size(); i++){
    QString filename=list.at(i).filePath();
    qDebug()<<"filename:"<<filename.mid(pos+1);
    QFile file(filename);
    if(file.exists()){
        file.open(QIODevice::ReadOnly);
        writer.addFile(filename.mid(pos+1), file.readAll());
        file.close();
    }
    else{
        qDebug()<<"file not exist:"<<filename;
    }
}
writer.close();
Kingly answered 12/10, 2021 at 11:18 Comment(1)
so sketchy. i love it.Wavelet
F
0

https://libzip.org/license/ has BSD licence you can use it anywhere

Example (On Linux) In .pro file:

HEADERS +=  /usr/include/zip.h

LIBS += -L/usr/lib/x86_64-linux-gnu/ -lzip

INCLUDEPATH += /usr/lib/x86_64-linux-gnu/libzip/include headers

Function that zip file:

int PutFileIntoZip(const QString &filePath)
{
    QFile regFile(filePath);
    Q_ASSERT(regFile.exists());
    const bool canOpenRegFile=regFile.open(QIODevice::ReadOnly);
    if(canOpenRegFile)
    {
        QString fileNameExpansion=filePath.section(QDir::separator(),-1,-1);
        const QStringRef fileName(&fileNameExpansion, 0, fileNameExpansion.length()-4);//22222.bin -> 22222
        int errorFlagCreateZip = 0;
        const QString zipPath=QDir::homePath()+QDir::separator()+fileName + ".zip";
        zip*const  zipHandle = zip_open(zipPath.toLatin1().constData(), ZIP_CREATE, &errorFlagCreateZip);
        if (ZIP_ER_OK==errorFlagCreateZip)
        {
            FILE* const fileC = fdopen(regFile.handle(), "r");
            zip_source* const  source = zip_source_filep(zipHandle, fileC, 0, regFile.size());
            if (source!=nullptr)
            {
                const zip_int64_t result = zip_file_add(zipHandle, fileNameExpansion.toLatin1().constData(), source, ZIP_FL_OVERWRITE);
                if (result>=0)
                {
                    zip_set_file_compression(zipHandle, result, ZIP_CM_DEFLATE64, 0);
                    qDebug()<< "Done";
                }
                else
                {
                    zip_source_free(source);
                }
            }
            int result = zip_close(zipHandle);
            fclose(fileC);
            regFile.close();
            return result;
        }
        else
        {
            zip_error_t error;
            zip_error_init_with_code(&error, errorFlagCreateZip);
            const int zipCreationErrorCode = zip_error_code_zip(&error);
            regFile.close();
            return zipCreationErrorCode;
        }
    }
    else
    {
        qDebug()<< regFile.errorString();
        return -1;
    }
}
Ferrule answered 12/7, 2023 at 11:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.