使用Qt压缩文件/文件夹

17

我想知道在Qt中是否有任何类可以压缩文件夹或文件。我尝试使用QProcess进行压缩,虽然成功压缩了,但我无法使用普通的zip工具解压它。请问有人知道如何使用Qt API类压缩一个文件夹/文件吗?

4个回答

36

几年前我遇到了这样的问题,以下是我的解决方案:

1). 获取QuaZip (链接在此:link text)

2). 将QuaZip源代码包含到您的项目文件中

头文件:

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 += 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). 添加头信息

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

4). 使用extract函数:

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;
}

存档功能:

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). 祝您愉快 ;)

更新: 针对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());
    }
}

7
QuaZip是LGPL协议的,因此您需要将主程序的许可证与LGPL兼容(例如LGPL或GPL),或者将QuaZip库作为单独的动态库文件分发,并且不能在程序中静态包含任何其代码。 - Mildred
如果你简单地跳过文件夹,QuaZip 在尝试解压缩时将无法列出它们。你需要创建一个以“/”结尾的空文件。请参见https://dev59.com/J2zXa4cB1Zd3GeqPR0Y9#24517029。 - gfrigon
考虑使用 include(thirdParty/quazip/quazip.pri)LIBS += -lz 将 QuaZip 导入到您的 Qt 项目中。 - Haozhe Xie
在recurseAddDir()中,如果文件isSymLink()为true,则会“return”。难道你不想改为“continue”吗?第一个SymLink将使您跳过目录中的所有其他文件。 - bwing

5

我正在使用Qt 5.12版本。

在.pro文件中,添加“QT+=gui-private”。

示例的cpp文件:

#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();

这很草图式,我喜欢它。 - Jason C

5

我认为你做不到。据我所知,qcompress和quncompress仅提供流等的压缩功能。这意味着它们不会创建真正zip文件所需的标头(也不会说明哪些文件在文件中)。

然而,有一个名为QuaZip的开源Qt库使用zlib(随qt一起提供),并提供了这个功能。


0

https://libzip.org/license/ 有 BSD 授权,您可以在任何地方使用它

示例(在 Linux 上) 在 .pro 文件中:

HEADERS +=  /usr/include/zip.h

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

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

压缩文件的功能:

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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接