QT 引入Quazip和Zlib源碼工程到項目中,無需編譯成庫,跨平臺,加密壓縮,帶有壓縮進度

前言

最近在做項目時遇到一個需求,需要將升級的文件壓縮成zip,再進行傳輸;

通過網絡調研,有許多方式可以實現,例如QT私有模塊的ZipReader、QZipWriter;或者第三方庫zlib或者libzip或者quazip等;其中libzip和quazip都是依賴于zlib;

然后,這些都需要變成庫,然后再鏈接到QT項目中進行使用;

由于項目的特殊性,有可能需要在window,Linux和ARM三個平臺上去運行,如果都編譯為庫去使用的話,那么需要編譯三個平臺,甚是麻煩,而且如果系統版本不一樣,可能還不能用;

所以,現階段有兩種方案,其一是使用QT自帶的壓縮庫,其二是使用第三方庫的源碼;

由此可知,使用QT自帶的私有庫是最方便,且網絡上也有教程(文章最后也提供的操作代碼);

但是,使用QT自帶的私有庫有幾個缺點,第一是需要QT5.14以上的版本才可以使用,且在QT6中已經移除了該模塊;而且需要安裝QT時勾選安裝QT源碼才可以使用;由此,這種方式pass掉;

本人在ARM平臺安裝QT,是使用命令行在線安裝的,QT的版本無法控制,也無法安裝源碼!

只能使用第二種方式了,遂在網絡上尋找第三方庫的源碼,如何將將源碼潛入到QT項目中去;

網絡上教程,百分之99.99都是叫你如何編譯使用的,剩下百分之0.01是需要vip和付費資源,完全沒有使用源碼的方式;

直到我遇到了一篇博客,里面博主就是使用Quazip+Zlib源碼,用于異步壓縮文件為zip格式,且博主將代碼開源,正好符合我的需求,再次非常感謝該博主,鏈接如下:

Qt+Quazip+Zlib源碼工程實現帶進度同步異步加解壓文件_qt 壓縮zip 進度-CSDN博客

但是,該項目源碼也使用到了QT的私有模塊,對于我這種在ARM平臺下使用命令行安裝的QT,且版本沒有達到5.14的我來說,無疑是晴天霹靂;

項目無法運行,報錯!!!

沒辦法,只能自己手動去修改,這難不倒身為程序員的我!

我將代碼中的私有模塊去掉,將博主寫的壓縮解壓縮代碼也去掉,自己動手寫了一個操作類,使用了Quazip的接口,供外部使用!(同步 壓縮/解壓縮)

完成后,突然想起來,還有加密壓縮的需求,這該怎么辦呢?

在網絡上調研了一整天,沒啥博客能講明白或者能使用quazip庫直接給出代碼的;

直到我遇到了另一篇博客,里面有分享了一些?加密壓縮多個文件 和?解壓含密碼的壓縮文件 的案例;

通過參考這些案例,我寫出來自己的加密壓縮和解密壓縮文件和文件夾的操作函數,經過測試可行;

再次也非常感謝該博主的分享,博文鏈接如下:

Qt加密壓縮與解壓-CSDN博客

到了這一步,項目需求的加密壓縮和解密壓縮基本就完成了;

但轉念一想,平常事我們使用的解壓縮軟件時,都是帶有進度條的,也就是說下解壓縮時,我們應該需要知道進度;

于是乎,再次修改代碼,增加了解壓縮操作時,將當前進度通過信號發射出去,由調用方處理這個信號即可!

因為我實現的解壓縮功能是同步的,所以,調用方在處理解壓縮時,如果不希望卡屏,就得使用線程去操作;

由此寫出了一個簡易的解壓縮zip軟件,運行效果如下:

壓縮成功后

解壓縮和壓縮文件是一樣的,這里就不放出演示了!

對比一下壓縮后的大小:

效果還是很明顯的!


目錄

前言

一、基礎的解壓縮

二、自定義實現解壓縮

1.加密壓縮

1).打開zip文件

2).獲取需要壓縮的文件夾內部的所有文件及子文件

3).創建一個QuaZipFile?

4).遍歷獲取到的所有文件列表

5).創建新的ZIP文件條目信息

6).打開ZIP文件條目

7).讀取寫入

8).關閉文件

9).詳細代碼

2.解密解壓縮

1).創建文件夾

?2).打開zip文件

3).獲取zip內的所有文件條目

4).遍歷這個鏈表

5).處理文件夾

?6).創建一個QuaZipFile?

7).打開QuaZipFile

8).讀取寫入(解壓縮)

?9).關閉文件

10).詳細代碼

三、源碼分享

1.下載后如何引入自己的項目工程中?

1).拷貝quazip文件夾到自己的項目工程中

2).在自己的項目的.pro文件引入如下代碼

3).包含頭文件

2.QT自帶壓縮庫操作分享 ZipReader、QZipWriter

1).導入私有模塊

2).zipcompress.h

3).zipcompress.cpp

四、結束語


一、基礎的解壓縮

網絡上介紹Quazip庫使用的,基本都是以下這些:

JlCompress mJlCompress;// 壓縮文件
mJlCompress.compressFiles(fileCompressed,files);// 解壓文件夾
mJlCompress.extractDir(fileCompressed, dir);// 壓縮文件夾
mJlCompress.compressDir(fileCompressed, dir, recursive);// 解壓文件
mJlCompress.extractFiles(fileCompressed, files);

其實如果是基礎用法,上面這幾句代碼也夠用了;

但是如果涉及到加密壓縮和進度方面,就得自己去實現解壓縮的過程了,且這樣也更加靈活一點!

二、自定義實現解壓縮

Quazip庫還提供了QuaZipQuaZipFileQuaZipNewInfoQuaZipFileInfo

等類供我們自己去處理!

QuaZip 類用于打開一個.zip文件;

QuaZipFile 類用于處理zip內部的文件,即讀取zip內部文件數據(解壓)或者往zip內部寫入文件(壓縮);【在此可以進行加密和解密處理】

QuaZipNewInfo 類用于定義一個zip的文件信息,在壓縮時使用;

QuaZipFileInfo 類用于處理一個zip內部的文件;

當然,還有以下這么多:(別家復制過來的)

說明
JlCompress典型操作工具類
QuaAdler32Adler32算法校驗和
QuaChecksum32校驗和接口
QuaCrc32CRC32校驗和
QuaGzipFileGZIP 文件操作
QuaZIODevice壓縮/解壓 QIODevice
QuaZipZIP 文件
QuaZipDirZIP文件內目錄導航
QuaZipFileZIP文件內的文件
QuaZipFileInfoZIP壓縮包內的文件信息
QuaZipFilePrivateQuaZip的接口
QuaZipNewInfo被創建的文件信息
QuaZipPrivateQuaZIP內部類

下面是加密壓縮和解密壓縮的過程分析;

1.加密壓縮

這里處理的是壓縮文件夾,壓縮文件與之類似;

壓縮后的文件結構與原來一致;

1).打開zip文件

QuaZip newZip(fileCompressed); // 要生成的zip文件                         
if (!newZip.open(QuaZip::mdCreate)) {                               qDebug() << "Failed to create ZIP file:" << fileCompressed;                                 return false;                                                  
}

參數枚舉介紹:

enum Mode {mdNotOpen,        ///< ZIP文件未打開。這是初始模式。mdUnzip,          ///< ZIP文件已打開,用于讀取其內部的文件。mdCreate,         ///< ZIP文件已通過open()調用創建。mdAppend,         ///< ZIP文件以追加模式打開。這指的是ZIP/UNZIP包中的APPEND_STATUS_CREATEAFTER模式,///  意味著zip被追加到某個已存在的文件中,該文件包含自解壓代碼。這顯然不是您想要用來///  向現有的ZIP歸檔添加文件的模式。mdAdd             ///< ZIP文件已打開,用于向歸檔中添加文件。
};

一般來說,我們只需要用到 mdUnzip 用于解壓縮 和 mdCreate 用于壓縮即可!

2).獲取需要壓縮的文件夾內部的所有文件及子文件

QStringList filePathList = 獲取所有文件();

3).創建一個QuaZipFile?

QuaZipFile zipFile(&newZip);

使用QuaZip作為參數;

4).遍歷獲取到的所有文件列表

在循環體內部處理壓縮事項;以下操作都是在循環體內部進行;

5).創建新的ZIP文件條目信息

QuaZipNewInfo info(文件名, 文件路徑+文件名);

6).打開ZIP文件條目

if (!zipFile.open(QIODevice::WriteOnly, info, password.toUtf8().constData(), 0, 8)) {  qDebug() << "Failed to open ZIP entry for writing:" << fileName;                            // 關閉ZIP                                                                                    newZip.close();                                                                                                                                return false;                                                                              
}

open參數介紹:

bool open(OpenMode mode,               // 打開模式,指定是以什么方式打開文件(例如,只寫、追加等)const QuaZipNewInfo& info,   // 文件信息,包括文件名、時間戳等const char *password = nullptr, // 密碼,用于加密文件,如果不需要加密則傳遞nullptrquint32 crc = 0,             // CRC校驗碼,通常設置為0,除非在原始模式下使用int method = Z_DEFLATED,     // 壓縮方法,Z_DEFLATED表示使用Deflate算法,Z_BZIP2ED表示使用BZIP2算法,0表示不壓縮int level = Z_DEFAULT_COMPRESSION, // 壓縮級別,對于Deflate算法,可以是0-9之間的值,Z_DEFAULT_COMPRESSION表示默認級別// 以下保持默認即可!!!bool raw = false,            int windowBits = -MAX_WBITS, int memLevel = DEF_MEM_LEVEL,int strategy = Z_DEFAULT_STRATEGY 
);

需要注意的是,

src參數,通常設置為0,除非你知道使用其他值,所帶來的后果;

method參數,需要設置為8,即宏Z_DEFLATED,這個是壓縮算法,注意,quazip只支持這種算法,所以必須設置為8;

level參數,是壓縮等級,可設置0-9,之間的任何一個數字,0表示無壓縮,最快,9表示最大壓縮,但最慢,一般設置為默認值即可!

其實也就是說,只需要設置前面三個參數即可!!!

7).讀取寫入

使用QFile讀取本地文件數據,寫入QuaZipFile中

QFile sourceFile(filePath);
sourceFile.open(QIODevice::ReadOnly);
zipFile.write(sourceFile.readAll());    // 注意,處理大文件時,這里會崩潰

注意,上面寫法在處理大文件時會有問題,readAll()函數會將文件數據讀如內存中,如果文件太大,會將堆內存給撐爆,程序就閃退了;

所以為了避免這樣的問題,在處理大文件時,必須分塊進行處理!

QByteArray buffer;                                    
while(!sourceFile.atEnd()) {                          buffer = sourceFile.read(4096);    // 每次讀取一頁內存數據          zipFile.write(buffer);                            
}

8).關閉文件

最后記得在循環尾部將文件關閉后,再進行新一輪的文件讀寫操作!

sourceFile.close();
zipFile.close();

處理完畢后,記得關閉 newZip.close();

加密壓縮文件部分與之類似,這里就不展開講解了!

給出函數接口,自己動手去實現一下吧;

    /*** @brief 加密壓縮多個文件* @param fileCompressed        壓縮包名* @param files                 要解壓的文件列表* @param password              密碼* @return*/bool compressFilesWithPassword(const QString &fileCompressed,const QStringList &files,const QString& password);

9).詳細代碼

進度部分就不詳細講解了,都在代碼函數中

/**                               * @brief 加密壓縮文件夾內的所有文件           * @param fileCompressed    壓縮包名-路徑  * @param dir               壓縮路徑  * @param password          密碼    * @return                        */                               
bool zipCompress::compressDirsWithPassword(const QString &fileCompressed, const QString &dir, const QString &password) {bool result = false;if (fileCompressed.isEmpty() || dir.isEmpty()) {emit signalCompressDirinish(result);return result;}QuaZip newZip(fileCompressed); // 要生成的zip文件if (!newZip.open(QuaZip::mdCreate)) {qDebug() << "Failed to create ZIP file:" << fileCompressed;emit signalCompressDirinish(result);return result;}// 讀取目錄中的所有文件QStringList filePathList = listFilesInDirectoryRecursively(dir);if (filePathList.isEmpty()) {qDebug() << "No files to compress in directory:" << dir;newZip.close();emit signalCompressDirinish(result);return result;}qreal pro = 100.0 / filePathList.size();qreal progress = 1.0;QuaZipFile zipFile(&newZip);for (const QString &filePath : filePathList) {// 構造相對于dir的文件名QString fileName = QDir(dir).relativeFilePath(filePath);// 創建ZIP文件條目信息QuaZipNewInfo info(fileName, filePath);// 嘗試打開ZIP文件條目進行寫入if (!zipFile.open(QIODevice::WriteOnly, info, password.toUtf8().constData(), 0, 8)) {qDebug() << "Failed to open ZIP entry for writing:" << fileName;// 關閉ZIPnewZip.close();emit signalCompressDirinish(result);return result;}// 打開源文件進行讀取QFile sourceFile(filePath);if (!sourceFile.open(QIODevice::ReadOnly)) {qDebug() << "Failed to open source file for reading:" << filePath;// 關閉ZIPnewZip.close();emit signalCompressDirinish(result);return result;}// 總進度emit signalCompressionProgress(progress, "1", filePath);// 大文件讀取分塊處理,否則程序會閃退{qreal file_pro = 100.0 / (sourceFile.size() / mBlockSize);qreal file_progress = 0.0;// 將源文件的內容寫入ZIP文件QByteArray buffer;while(!sourceFile.atEnd()) {buffer = sourceFile.read(mBlockSize);zipFile.write(buffer);file_progress += file_pro;emit signalFileProgress(file_progress, filePath);}emit signalFileProgress(100, filePath);}sourceFile.close();zipFile.close();progress += pro;progress = qBound(0.0, progress, 99.999);}result = true;newZip.close();emit signalCompressionProgress(100, "1", "");emit signalCompressDirinish(result);return result;
}QStringList zipCompress::listFilesInDirectoryRecursively(const QString &directoryPath) {QStringList list;QDirIterator it(directoryPath, QDir::Files, QDirIterator::Subdirectories);while (it.hasNext()) {QString filePath = it.next();list.append(filePath);}return list;
}

2.解密解壓縮

這里處理的是解壓縮一個zip文件;

解壓出來文件結構與壓縮包內一致!

1).創建文件夾

判斷傳進來的路徑解壓路徑,如果不存在則創建

?2).打開zip文件

QuaZip zip(fileCompressed);                                       
if (!zip.open(QuaZip::mdUnzip)) {                                 qDebug() << "Failed to open ZIP file:" << fileCompressed;                          return false;                                                
}

因為是解壓縮,所以參數使用mdUnzip

3).獲取zip內的所有文件條目

QList<QuaZipFileInfo> fileInfoList = zip.getFileInfoList();

4).遍歷這個鏈表

解壓縮操作都在這個循環體內進行;

5).處理文件夾

如果處理的當前文件,在本機電腦中還沒有路徑,則進行創建;

且跳過是文件夾的操作!

?6).創建一個QuaZipFile?

QuaZipFile zipFile(zip.getZipName(), 文件名);

7).打開QuaZipFile

zipFile.open(QIODevice::ReadOnly, password.toUtf8().constData());

參數二是密碼;

8).讀取寫入(解壓縮)

注意,為了防止大文件的問題,讀取寫入是還是分塊進行操作!?

QByteArray buffer;                                        
while(!zipFile.atEnd()) {                                 buffer = zipFile.read(4096);                    outFile.write(buffer);                                  
}

?9).關閉文件

最后記得在循環尾部將文件關閉后,再進行新一輪的文件讀寫操作!

outFile.close();    
zipFile.close();    
處理完畢后,記得關閉 zip.close();

10).詳細代碼

進度部分就不詳細講解了,都在代碼函數中

bool zipCompress::extractDirWithPassword(const QString &fileCompressed, const QString &dir, const QString &password) {bool result = false;if (fileCompressed.isEmpty() || dir.isEmpty()) {emit signalExtractDirFinish(result);return result;}// 使用QFileInfo獲取文件名(不帶擴展名)并構建目標目錄路徑QFileInfo fileInfo(fileCompressed);QString targetDirName = fileInfo.completeBaseName();QString targetDirPath = QDir(dir).absoluteFilePath(targetDirName);// 創建目標目錄(如果不存在)QDir targetDir(targetDirPath);if (!targetDir.exists() && !targetDir.mkpath(".")) {qDebug() << "Failed to create target directory:" << targetDirPath;emit signalExtractDirFinish(result);return result;}// 打開ZIP文件QuaZip zip(fileCompressed);if (!zip.open(QuaZip::mdUnzip)) {qDebug() << "Failed to open ZIP file:" << fileCompressed;emit signalExtractDirFinish(result);return result;}// 遍歷ZIP文件中的所有條目QList<QuaZipFileInfo> fileInfoList = zip.getFileInfoList();qreal pro = 100.0 / fileInfoList.size();qreal progress = 1.0;for (const QuaZipFileInfo &fileInfoEntry : fileInfoList) {// 組合目標文件路徑QString fileName = fileInfoEntry.name;QString filePath = targetDir.absoluteFilePath(fileName);// 如果是文件夾,如果不存在則創建;然后跳過文件夾的操作QFileInfo info(filePath);if (info.isDir()) {QDir().mkpath(filePath);continue;} else {    // 如果是文件,如果路徑不存在,創建該路徑QString path = info.path();QDir dpath(path);if (!dpath.exists()) {if (!dpath.mkpath(path)) {qDebug() << "Failed to create directory:" << path;emit signalExtractDirFinish(result);return result;}}}// 打開ZIP條目并解壓文件QuaZipFile zipFile(zip.getZipName(), fileName);if (zipFile.open(QIODevice::ReadOnly, password.toUtf8().constData())) {QFile outFile(filePath);if (!outFile.open(QIODevice::WriteOnly)) {qDebug() << "Failed to open output file for writing:" << filePath;zipFile.close();zip.close();emit signalExtractDirFinish(result);return result;}emit signalCompressionProgress(progress, "2", filePath);// 大文件讀取分塊處理,否則程序會閃退{qreal file_pro = 100.0 / (zipFile.size() / mBlockSize);qreal file_progress = 0.0;// 解壓并寫入文件QByteArray buffer;while(!zipFile.atEnd()) {buffer = zipFile.read(mBlockSize);outFile.write(buffer);file_progress += file_pro;emit signalFileProgress(file_progress, filePath);}emit signalFileProgress(100, filePath);}outFile.close();zipFile.close();} else {qDebug() << "Failed to open ZIP entry for reading:" << fileName;zip.close();emit signalExtractDirFinish(result);return result;}progress += pro;progress = qBound(0.0, progress, 99.999);}result = true;// 關閉ZIP文件zip.close();emit signalCompressionProgress(100, "2", "");emit signalExtractDirFinish(result);return result;
}

三、源碼分享

到此,解壓縮已經介紹完畢,現在分享我參考寫的解壓縮程序

https://download.csdn.net/download/cpp_learner/90411497https://download.csdn.net/download/cpp_learner/90411497因為我這邊編寫的壓縮和解壓縮操作都是同步的,所以,建議操作時使用線程去處理,否則會卡屏,而且進度條也無法使用;

在我給出的案例工程中,就是使用了線程去解壓縮等操作,有興趣可以學習一下!

1.下載后如何引入自己的項目工程中?

1).拷貝quazip文件夾到自己的項目工程中

2).在自己的項目的.pro文件引入如下代碼

# 源碼方式使用需要設置為靜態庫
DEFINES +=   QUAZIP_STATIC
include($$PWD/quazip/3rdparty/zlib.pri)
include($$PWD/quazip/quazip.pri)
include($$PWD/quazip/zipop/zipop.pri)

3).包含頭文件

#include "zipcompress.h"

頭文件包含后,就可以直接使用了?

compressionTool::zipCompress m_zipCompress;                                  
// 壓縮文件夾                                                                     
m_zipCompress.compressDirsWithPassword(mZipPath, mPath, mPassword);        // 解壓縮                                                                       
m_zipCompress.extractDirWithPassword(fileName, fileinfo.path(), "abc@123");  // 壓縮文件                                                                    
m_zipCompress.compressFilesWithPassword(mZipPath, mFiles, mPassword);      

2.QT自帶壓縮庫操作分享 ZipReader、QZipWriter

以下是我編寫好的操作類,直接新建文件拷貝代碼到文件中即可使用!

(注意,一般來說,需要QT5.14以上的版本才可以使用)

1).導入私有模塊

.pro文件中添加私有模塊

QT       += gui-private

2).zipcompress.h

#ifndef ZIPCOMPRESS_H
#define ZIPCOMPRESS_H#include <QObject>
#include <QtGui/private/qzipreader_p.h>
#include <QtGui/private/qzipwriter_p.h>class ZipCompress : public QObject
{Q_OBJECT
public:ZipCompress(QObject *parent = nullptr);/*** @brief 壓縮文件* @param fileNames 需要壓縮的文件名* @param saveName  壓縮后的文件路徑和名字*/bool zipCompressFile(const QStringList &fileNames, const QString &saveName);/*** @brief 壓縮文件夾* @param dirName   需要壓縮的文件夾名* @param saveName  壓縮后的文件路徑和名字*/bool zipCompressDirector(const QString &dirName, const QString &saveName);/*** @brief 解壓縮* @param zipFile   壓縮包* @param saveDir   解壓路徑* @return*/bool zipUnCompressFile(const QString &zipFile, const QString &saveDir);private:/*** @brief 遞歸壓縮* @param zipWriterHandle       QZipWriter指針* @param zipParentDirName      父路徑,相對路徑* @param srcDir                當前需要處理的文件夾路徑,絕對路徑*/void zipCompressDirector(void *zipWriterHandle, const QString &zipParentDirName, const QString &srcDir);private:QZipReader *mZipReader;QZipWriter *mZipWriter;
};#endif // ZIPCOMPRESS_H

3).zipcompress.cpp

#include "zipcompress.h"#include <QFileInfo>
#include <QDir>
#include <QDebug>ZipCompress::ZipCompress(QObject *parent): QObject(parent)
{
}bool ZipCompress::zipCompressFile(const QStringList &fileNames, const QString &saveName)
{QZipWriter *zipWriter = new QZipWriter(saveName);if (!zipWriter) {qDebug() << "壓縮失敗!";return false;}QFile file;QFileInfo fileInfo;foreach (const QString &fileName, fileNames) {if (fileName.isEmpty() || "" == fileName) {continue;}// 判斷文件是否存在fileInfo.setFile(fileName);if (!fileInfo.exists()) {qDebug() << fileName << " : 文件不存在!";continue;}if (fileInfo.isFile()) {// 打開文件file.setFileName(fileName);if (!file.open(QIODevice::ReadOnly)) {qDebug() << fileName << " : 文件打開失敗!";continue;}// 添加到壓縮包中zipWriter->addFile(fileInfo.fileName(), file.readAll());file.close();} else if (fileInfo.isDir()) {// 添加到壓縮包中zipWriter->addDirectory(fileName);}}zipWriter->close();if (zipWriter) {delete zipWriter;zipWriter = nullptr;}return true;
}bool ZipCompress::zipCompressDirector(const QString &dirName, const QString &saveName)
{QFile file(saveName);if (!file.open(QFile::WriteOnly)) {qDebug() << "壓縮失敗!";return false;}std::shared_ptr<QZipWriter> zipWriter(new QZipWriter(&file));if (!zipWriter) {qDebug() << "壓縮失敗!";return false;}zipCompressDirector(zipWriter.get(), "", dirName);zipWriter->close();file.close();return true;
}bool ZipCompress::zipUnCompressFile(const QString &zipFile, const QString &saveDir)
{QFile file(zipFile);if (!file.open(QIODevice::ReadOnly)) {qDebug() << zipFile << " : 文件打開失敗!";return false;}QDir dir(saveDir);if (!dir.exists()) {if (!dir.mkpath(saveDir)) { // 創建文件夾qDebug() << saveDir << " : 文件夾路徑不存在,且創建失敗!";return false;}}std::shared_ptr<QZipReader> zipReader(new QZipReader(&file));return zipReader->extractAll(saveDir);  // 全部解壓出來
}void ZipCompress::zipCompressDirector(void *zipWriterHandle, const QString &zipParentDirName, const QString &srcDir)
{QZipWriter *zipWriter = (QZipWriter *)zipWriterHandle;QDir directory(srcDir);QFileInfoList fileList = directory.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);for (const QFileInfo &fileInfo : fileList) {QString relatePath = fileInfo.fileName();       // 獲取當前文件名 | 文件夾名if (!zipParentDirName.isEmpty()) {relatePath.prepend(zipParentDirName + "/"); // 頭部拼接上父路徑,相對路徑}QString filePath = fileInfo.absoluteFilePath(); // 獲取當前文件的絕對路徑if (fileInfo.isDir()) {// 添加文件夾zipWriter->addDirectory(relatePath);// 參數二:文件夾相對路徑      參數三:文件夾絕對路徑zipCompressDirector(zipWriter, relatePath, filePath);} else {QFile file(filePath);if (!file.open(QIODevice::ReadOnly)) {qDebug() << filePath << " : 文件打開失敗!";continue;}zipWriter->addFile(relatePath, file.readAll());file.close();}}
}

四、結束語

文章最后,感謝各位能夠堅持看完,相信此篇文章也會對你有所幫助;

當然,我上面所給出的只是一些基礎用法,還有很多高級用法我也還沒整明白;

或者哪位大佬有更好的實現方式,也歡迎評論區分享出來,供大家一起探討學習;

完!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/896417.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/896417.shtml
英文地址,請注明出處:http://en.pswp.cn/news/896417.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[oAuth2授權]Web前端+NodeCoze API Web后端程序+Coze授權服務器工作流程架構流程圖詳解

嗯,用戶之前已經了解了如何使用React和Node.js結合Coze API實現OAuth2授權,現在他們具體想實現的是在Web應用中,當用戶點擊一個按鈕(比如“和Bot對話”)時,觸發授權流程,重定向到Coze的授權服務器獲取code。用戶還提供了一個具體的cURL請求示例,展示了如何通過302重定向…

Fiddler在Windows下抓包Https

文章目錄 1.Fiddler Classic 配置2.配置瀏覽器代理自動代理手動配置瀏覽器代理 3.抓取移動端 HTTPS 流量&#xff08;可選&#xff09;解決抓取 HTTPS 失敗問題1.Fiddler證書過期了 默認情況下&#xff0c;Fiddler 無法直接解密 HTTPS 流量。需要開啟 HTTPS 解密&#xff1a; 1…

vue:vite 代理服務器 server: proxy 配置

Vite 代理服務器&#xff08;Proxy&#xff09;的配置通常用于開發環境&#xff0c;以解決跨域請求等問題。以下是一個詳細的配置步驟&#xff1a; 通過以上步驟&#xff0c;你就可以在 Vite 項目中配置代理服務器&#xff0c;以便在開發過程中方便地訪問后端服務。 ?找到 Vi…

DINOv2 + yolov8 + opencv 檢測卡車的可拉拽雨覆是否完全覆蓋

最近是接了一個需求咨詢圖像處理類的&#xff0c;甲方要在卡車過磅的地方裝一個攝像頭用檢測卡車的車斗雨覆是否完全&#xff0c; 讓我大致理了下需求并對技術核心做下預研究 開發一套圖像處理軟件&#xff0c;能夠實時監控經過的卡車并判斷其車斗的雨覆狀態。 系統需具備以下…

AI除了可以能提高工作效率的還可以實現哪些功能?

AI除了能顯著提高工作效率之外&#xff0c;其在眾多領域的潛力遠不止于此。 在教育領域&#xff0c;AI正逐步成為個性化教學的得力助手。通過分析學生的學習習慣和能力水平&#xff0c;AI能夠定制出最適合每個學生的學習計劃&#xff0c;不僅提升了學習效率&#xff0c;還極大地…

vscode settings(一):全局| 用戶設置常用的設置項

參考資料 Visual Studio Code權威指南 by 韓駿 一. 全局設置與用戶設置 1.1 Vscode支持兩種不同范圍的設置 用戶設置(User Settings)&#xff1a;這是一個全局范圍的設置&#xff0c;會應用到所有的Visual Studio Code實例中。工作區設置(Workspace Settings)&#xff1a;設…

ROS的action通信——實現階乘運算(二)

在ROS中除了常見的話題(topic&#xff09;通信、服務(server)通信等方式&#xff0c;還有action通信這一方式&#xff0c;由于可以實時反饋任務完成情況&#xff0c;該通信方式被廣泛運用于機器人導航等任務中。本文將通過三個小節的分享&#xff0c;實現基于action通信的階乘運…

Spring Boot啟動過程?

目錄 1. 啟動入口 2. SpringApplication 初始化 3. 準備環境 4. 創建應用上下文(ApplicationContext) 5. 準備應用上下文 6. 刷新應用上下文 7. 啟動 Web 服務器(若為 Web 應用) 8. 發布 ApplicationStartedEvent 事件 9. 執行 Runner 10. 發布 ApplicationReady…

VUE 獲取視頻時長,無需修改數據庫,前提當前查看視頻可以得到時長

第一字段處 <el-table-column label"視頻時長" align"center"> <template slot-scope"scope"> <span>{{ formatDuration(scope.row.duration) }}</span> </template> </el-ta…

seacmsv9注入管理員賬號密碼+orderby+limit

一、seacmsv9 SQL注入漏洞 1.1 seacms漏洞介紹 海洋影視管理系統&#xff08;seacms&#xff0c;海洋cms&#xff09;是一套專為不同需求的站長而設計的視頻點播系統&#xff0c;采 用的是 php5.Xmysql 的架構&#xff0c;seacmsv9漏洞文件&#xff1a;./comment/api/index.p…

WPF學習之Prism(二)

前言 學習一下Prism。 1.Prism Prism框架提供了一套豐富的工具、類和模塊&#xff0c;幫助開發人員實現以下功能&#xff1a; 模塊化&#xff1a;Prism框架支持將應用程序拆分為多個模塊&#xff0c;每個模塊具有自己的功能和視圖。這種模塊化的設計使得應用程序更加靈活和…

【EB-03】 AUTOSAR builder與EB RTE集成

AUTOSAR builder與EB RTE集成 1. Import Arxml files to Tresos2. Run MultiTask Script3. Add Components3.1 Run EcuExtractCreator Script4. Mapping Component to Partitions5. Event Mapping/Runnables Mapping to Tasks6. Port Connect7. Run SvcAs_Trigger Script8. Ver…

算法教程:香檳塔問題

香檳塔問題 問題描述 我們將玻璃杯堆成金字塔狀,第一排有 1 個玻璃杯,第二排有 2 個玻璃杯,依此類推,直到第 100 排。每個玻璃杯裝一杯香檳。 然后,將一些香檳倒入最上面的第一個玻璃杯中。當最上面的玻璃杯裝滿時,任何多余的液體都會均勻地落到它左右兩側的玻璃杯上。當…

FastJSON 默認行為:JSON.toJSONString 忽略 null 字段

完整的 FakeRegistrationController 代碼&#xff0c;這讓我可以全面分析后端邏輯&#xff0c;特別是為什么空的字段&#xff08;如 compareDate&#xff09;不返回給前端。我將詳細分析代碼的每個接口&#xff0c;尤其是與 list 請求和字段返回相關的部分&#xff0c;并解釋原…

大模型基礎概念之神經網絡寬度

在大模型中,神經網絡寬度是提升模型容量的核心手段之一,與深度、數據規模共同構成性能的三大支柱。合理增加寬度可顯著增強模型表達能力,但需結合正則化、硬件優化和結構設計進行平衡。未來趨勢可能包括動態寬度調整、稀疏化寬度設計(如MoE)以及更高效寬度-深度復合縮放策…

在Linux環境下利用MTCNN進行人臉檢測(基于ncnn架構)

概述 本文將詳細介紹如何在Linux環境下部署MTCNN模型進行人臉檢測&#xff0c;并使用NCNN框架進行推理。 1. CMake的安裝與配置 下載CMake源碼 前往CMake官網下載&#xff0c;找到適合您系統的最新版本tar.gz文件鏈接&#xff0c;或者直接通過wget下載&#xff1a;CMake官方…

算法day1 dfs搜索2題

一 火星人 拿到這種類似于排序的&#xff0c;這個就好比如我們之前學習dfs基礎的時候里面的指數型枚舉 指數型枚舉數據與數據之間沒有任何枚舉&#xff0c;就比如選這個數字與不選組合型枚舉數據與數據之間有聯系&#xff0c;下一個數字不可以給上一個數字排列型枚舉數據與數…

CC攻擊防御策略全解析:技術實現與代碼示例

CC攻擊&#xff08;Challenge Collapsar&#xff09;是一種以消耗服務器資源為目標的分布式拒絕服務攻擊&#xff08;DDoS&#xff09;&#xff0c;其特點在于攻擊流量偽裝成合法請求&#xff0c;難以通過傳統防火墻完全防御。本文將從技術實現角度詳細解析CC攻擊的防御策略&am…

(九)axios的使用

1、axios 的基本使用 1.1、簡介 在 Web 開發的演進歷程中&#xff0c;數據請求方式的變革至關重要。回溯早期&#xff0c;舊瀏覽器在向服務器請求數據時&#xff0c;存在嚴重弊端。由于返回的是整個頁面數據&#xff0c;每次請求都會導致頁面強制刷新&#xff0c;這不僅極大地…

【MySQL篇】數據庫基礎

目錄 1&#xff0c;什么是數據庫&#xff1f; 2&#xff0c;主流數據庫 3&#xff0c;MySQL介紹 1&#xff0c;MySQL架構 2&#xff0c;SQL分類 3&#xff0c;MySQL存儲引擎 1&#xff0c;什么是數據庫&#xff1f; 數據庫&#xff08;Database&#xff0c;簡稱DB&#xf…