☰
QuaZip预编译库接入指南:Qt版本、MSVC/MinGW选型与DLL部署避坑
2026/10/16 6:45:37 网站建设 项目流程

简介:QuaZip是开源的C++压缩处理库,支持ZIP档案的读取、创建、更新,并对RAR格式提供基础操作能力。这份已编译好的lib与dll资源包,面向Windows环境下使用Qt5或Visual Studio的C++开发者,帮助其快速获得可用的压缩模块,免去自行编译源码、配置Qt依赖的繁琐过程。压缩包共29个文件,包含头文件(h)、CMake配置、静态库(lib)、动态库(dll)及pkg-config文件,整体仅427KB,按include、lib、bin目录组织,结构与Qt5常见安装布局一致,便于工程直接引用。已有599人学习下载;链接lib后还需将对应dll置于运行目录或PATH中,即可开始使用。静态库与动态库版本齐全,可灵活选择链接方式;借助QuaZip和QuaZipFile等接口,能直接实现ZIP档案遍历、解压、添加与删除,并借助错误处理机制排查传输或读写异常,适合中高级C++开发者用于压缩相关模块的开发。

1. QuaZip 编译好的库 lib、dll:先对齐版本再接线,少走三小时弯路

给 C++/Qt 项目加 ZIP 压缩解压,QuaZip 是比直接调 zlib 舒服得多的封装层。下载一份别人编译好的 QuaZip 库,lib、dll 都摆在眼前,理论上开箱即用。但我见过太多人卡在这一步:拿到库直接往工程里塞,链接报错一串,查来查去发现是 Qt 版本、编译器、位数,甚至 Debug/Release 有一个对不上,整个下午就耗在“黑匣子”里翻车。QuaZip 编译产物跟 Qt 版本强绑定,对不上就是编译通过、运行崩溃。这篇文章从编译原理讲起,把预编译库的选型、接入、部署和排错流程完整拆一遍,适合想绕开自编译的 Qt 开发者,也适合被 CMake 折腾到头秃、只想拿 lib、dll 快速交付的老手。读完你就能准确判断资源包里哪一份库是你当前工程真正该用的。

2. 自己走一遍 QuaZip 编译:CMake 参数、zlib 依赖和 MSVC/MinGW 产物差异

很多拿到预编译库的人第一反应是“能用就行”,等到对不上号时才回头补课。与其到时候靠玄学猜,不如先把 QuaZip 的编译链路看明白。搞清这一步,后面接入和排错会顺很多。

2.1 QuaZip 源码、zlib 和 Qt:编译 QuaZip 的三件套

QuaZip 本身是一组 C++ 类的集合,核心类包括QuaZip、QuaZipFile、QuaZipFileInfo、QuaZipDir、QuaZipNewInfo。它不自己实现压缩算法,底层压的是 zlib,对上层的 IO 抽象则依赖 QtCore。也就是说,编译 QuaZip 至少需要三样东西:QuaZip 源码、zlib 的头文件与库、以及一套和你目标工程一致的 Qt 环境。

提示:QuaZip 是 zlib 的 Qt 封装,编译时要同时满足 zlib 和 QtCore 两个依赖,任何一个版本错位,出来的 lib、dll 都是残品。

我在实际项目里习惯把编译产物按“头文件、导入库、动态库”三部分理解:头文件负责编译期类型检查,导入库负责链接器找符号,动态库负责程序运行时真正干活。预编译资源包里通常也是这种结构,接入工程时三部分都要放对,缺一个都会出问题。

2.2 一条 CMake 命令编译 QuaZip:参数和产物路径

自己编译 QuaZip 其实不复杂,前提是你已经装好了 Qt 和 CMake。源码包根目录一般自带CMakeLists.txt,常见做法是开一个 build 目录,把构建配置和源码目录分开:

cmake -B build -S . \ -DCMAKE_PREFIX_PATH="C:/Qt/6.5.3/msvc2019_64" \ -DQUAZIP_BUILD_SHARED=ON \ -DQUAZIP_BUILD_TESTS=OFF cmake --build build --config Release --target quazip

说明:CMAKE_PREFIX_PATH用来告诉 CMake “Qt 装在哪个目录”,CMake 会从里面找 Qt 的 CMake config 文件,路径填错的话第一步就会报找不到 Qt;QUAZIP_BUILD_SHARED=ON表示生成动态库,也就是我们要的 quazip.dll;QUAZIP_BUILD_TESTS=OFF是关掉测试目标,避免构建链上拖进 Qt Test 等额外依赖。构建完成后,Release 配置下会生成动态库和导入库,如果希望在工程里直接用这份产物,后续配合第 3 章的目录规划放入工程即可。

2.3 MSVC 与 MinGW:两套 lib、dll 无法互用

QuaZip 在不同工具链下生成的库文件名不一样,这点是接入预编译库最容易踩的坑。MSVC 工具链产出的是quazip.lib加quazip.dll;MinGW 工具链产出的是libquazip.dll.a加quazip.dll。也就是说,动态库文件名可能相同,但导入库完全不同,链接器不认识对方的格式。

工具链导入库动态库附带依赖
MSVC(VS2019/2022)quazip.libquazip.dllmsvcp140.dll、vcruntime140.dll
MinGW(Qt 自带工具链)libquazip.dll.aquazip.dlllibgcc_s_seh-1.dll、libstdc++-6.dll

所以接入前一定要确认工程当前的套件是 MSVC 还是 MinGW。有人把 MSVC 编译的库硬塞到 MinGW 工程里,CMake 配置阶段不报错,链接阶段直接黑盒报错,就是因为导入库格式不兼容。Qt 的安装目录命名通常会有提示,比如msvc2019_64和mingw_64,这个信息比配置文件里的任何文字都可靠。

2.4 既然能编译,为什么还推荐用预编译好的库

自己编译 QuaZip 的优势是版本可控,但代价也很明显:本地要有匹配的 Qt、zlib、CMake 工具链,还要花几分钟处理各种环境变量。对大多数 C++/Qt 应用开发来说,压缩解压只是附属功能,不值得为它搭一整条编译链。

这也是预编译库资源的价值所在。人家已经把工具链的坑趟平了,你只需要关心当前工程是 Qt5 还是 Qt6、MSVC 还是 MinGW、x86 还是 x64,然后把对应那一份 lib、dll 拿出来接线。这份精力省下来,可以去处理更重要的业务逻辑。

3. 把预编译 QuaZip 库接进 Qt 工程:三条接线方式与运行时 DLL 处理

拿到预编译库以后,第一个动作不是写代码,而是核对版本。这一步做扎实了,后面所有链接错误都能避免。我一般会把 quazip 相关文件统一放进工程下的3rdparty/quazip目录,方便以后升级替换。

3.1 动手之前,核对四件事

接入前先确认四组信息:Qt 主版本号、编译器类型、目标位数、Debug/Release 模式。

常见的确认方法是打开命令行跑qmake -v,看 qmake 输出里的 Qt 版本;再看 Qt 安装目录下的套件名称,比如msvc2019_64就代表 MSVC 2019 64 位,mingw_64代表 MinGW 64 位。至于 Debug/Release,看路径里是debug还是release目录,或者看文件名,很多预编译库会在 debug 库文件名后面带d后缀。

提示:这四项里任何一项不一致,都不建议继续往下接。运气好只是编译告警,运气差就是第十九层链接错误。

3.2 CMake 接线:用 IMPORTED 目标把库导进工程

CMake 工程接入预编译库,最稳的方式不是直接link_directories,而是把 quazip 定义成一个导入目标,这样头文件、导入库、DLL 路径都能集中在一个地方管理:

add_library(quazip SHARED IMPORTED) set_target_properties(quazip PROPERTIES IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/3rdparty/quazip/bin/quazip.dll" IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/3rdparty/quazip/lib/quazip.lib" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/3rdparty/quazip/include" ) target_link_libraries(app PRIVATE quazip)

说明:IMPORTED_LOCATION指向运行时 DLL,IMPORTED_IMPLIB指向链接用的导入库,MSVC 下是quazip.lib,MinGW 下要改成libquazip.dll.a。INTERFACE_INCLUDE_DIRECTORIES指定 QuaZip 头文件目录,这样依赖 quazip 的目标会自动带上 include 路径。这里只做了导入,真正的 DLL 部署还要看 3.4 节。

3.3 qmake 接线:INCLUDEPATH 与 LIBS 的 Debug/Release 分支

还在用.pro工程的 Qt 项目,配置更直接,但 Debug/Release 的库分支建议提前写好,不然换模式编译时又得改一次:

INCLUDEPATH += $$PWD/3rdparty/quazip/include CONFIG(debug, debug|release) { LIBS += -L$$PWD/3rdparty/quazip/lib/debug -lquazip } else { LIBS += -L$$PWD/3rdparty/quazip/lib/release -lquazip }

说明:-L指定库搜索路径,-lquazip让链接器去找quazip.lib(MSVC)或libquazip.dll.a(MinGW)。写成CONFIG(debug, debug|release)分支是为了让 qmake 自动选择对应模式下的库,避免把 debug 库链到 release 工程里,这种错位导致的链接报错非常难查。

3.4 运行时:quazip.dll 要待在 exe 旁边,不只是链接完就没事

链接通过只代表编译期完成,程序运行时要加载quazip.dll,Windows 下 DLL 的搜索顺序是程序所在目录、系统目录、PATH 环境变量。最常见的坑是链接时用.lib导入了,但运行目录里没有quazip.dll,程序启动瞬间报“找不到 quazip.dll”。

Qt 自带一个部署工具windeployqt,可以帮你把 Qt 相关 DLL 全部复制到 exe 目录:

windeployqt --release --no-translations app.exe

但要注意,windeployqt只处理 Qt 自身的依赖,QuaZip 不是 Qt 官方插件,它不会自动复制quazip.dll。如果这份预编译库是动态链接 zlib,还要把zlib1.dll一并带过去。我一般在windeployqt之后手动补一句复制命令,把3rdparty/quazip/bin下的所有 DLL 拷到构建输出目录。

4. QuaZip 核心调用:用 QuaZipFile 完成目录压缩、解压与分块读写

预编译库接入工程只是第一步,真正检验成果的是调用代码。QuaZip 的 API 设计得比裸 zlib 友好得多,能让你像操作普通文件一样操作 zip 包内部的条目。

4.1 对象模型:QuaZip 是 zip 外壳,QuaZipFile 是里面的文件句柄

先理清两个核心类的关系。QuaZip代表一个 zip 归档文件,负责打开、关闭、遍历归档内部的条目;QuaZipFile则是归档内的单个文件句柄,你可以把它当QFile一样读写。QuaZip打开时有三种模式:mdCreate创建新归档,mdUnzip读取现有归档,mdAppend在已有归档上追加内容。

写压缩代码时,顺序是固定的:先创建一个QuaZip对象,打开为mdCreate;再创建QuaZipFile,用open方法开始一个新条目;写入数据后关闭QuaZipFile,继续下一个;最后关闭QuaZip。

4.2 压缩一个目录:从零开始打包,带路径前缀和中文名

假设有一个本地目录D:/data/images,你想把它完整打进一个 zip 包,保留相对路径结构。下面这个函数实现了基本逻辑:

bool zipDirectory(const QString& zipPath, const QString& srcDir) { QuaZip zip(zipPath); if (!zip.open(QuaZip::mdCreate)) { return false; } QDirIterator it(srcDir, QDir::Files, QDirIterator::Subdirectories); while (it.hasNext()) { QString fullPath = it.next(); QString relativePath = QDir(srcDir).relativeFilePath(fullPath); relativePath.replace('\\', '/'); QuaZipFile zipFile(&zip); if (!zipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(relativePath, fullPath))) { zip.close(); return false; } QFile inFile(fullPath); inFile.open(QIODevice::ReadOnly); char buf[8192]; qint64 got; while ((got = inFile.read(buf, sizeof(buf))) > 0) { zipFile.write(buf, got); } zipFile.close(); inFile.close(); } zip.close(); return true; }

说明:QuaZipNewInfo(relativePath, fullPath)第一个参数是 zip 包内的路径,第二个参数是本地文件路径,用来读取文件修改时间写入 zip 头;relativePath.replace('\\', '/')是把 Windows 路径分隔符转成 zip 标准要求的斜杠。读取本地文件时用了 8KB 缓冲区循环写入,而不是readAll(),处理几十 MB 的文件时能避免一次性占用大量内存。

4.3 解压 Zip 到目录:按条目逐一落盘,先检查 zip 条目冲突

解压逻辑反过来走。先以mdUnzip打开归档,拿到所有条目信息,再逐个创建QuaZipFile读取内容:

bool unzipToDir(const QString& zipPath, const QString& destDir) { QuaZip zip(zipPath); if (!zip.open(QuaZip::mdUnzip)) { return false; } zip.setFileNameCodec("UTF-8"); QList<QuaZipFileInfo> entries = zip.getFileInfoList(); for (const QuaZipFileInfo& entry : entries) { if (entry.name.endsWith('/')) { continue; } QString outPath = QDir(destDir).filePath(entry.name); QFileInfo info(outPath); if (!QDir().mkpath(info.absolutePath())) { zip.close(); return false; } QuaZipFile zipFile(&zip); if (!zipFile.open(QIODevice::ReadOnly, entry.name)) { zip.close(); return false; } QFile outFile(outPath); outFile.open(QIODevice::WriteOnly); outFile.write(zipFile.readAll()); zipFile.close(); outFile.close(); } zip.close(); return true; }

说明:getFileInfoList()返回归档内全部条目,比逐条goToFirstFile()更适合需要预判目录结构的使用场景。entry.name.endsWith('/')是为了跳过目录条目,这类条目在 zip 里只是标记,没有实际文件内容。解压到本地前用mkpath确保父目录存在,避免文件流打开失败。中文文件名需要调用setFileNameCodec("UTF-8"),否则部分工具打出的 zip 包里的中文名会乱码。

4.4 别一次 readAll 大文件:分块写入避免内存翻车

上面解压代码为了简洁用了readAll(),如果你确认压缩包内都是小文件,这样没问题。但如果要处理几十 MB 甚至上百 MB 的单个文件,readAll()会把整个文件内容一次性加载进内存,长时间运行的进程内存直接冲高。

我在某个处理批量图片的项目里就吃过这个亏,压缩包内单张图都挺大,几十张图全部 readAll 后进程内存飙到 600 多 MB。后来改成统一的QuaZipFile::read循环分块写入,内存占用降到 20 MB 以内才算正常。预编译库只是解决了链接问题,代码里的资源管理还是得自己把关。

5. 避坑:QuaZip 预编译库在链接、部署与中文字符集上的五个典型问题

接入 QuaZip 预编译库,链接和运行阶段最容易出问题。下面是我在多个项目里反复遇到的五个场景,每一条都按“现象、原因、解决”拆开讲,希望能帮你少走一次弯。

5.1 场景一:LNK2038 检测到 _ITERATOR_DEBUG_LEVEL 不匹配

现象:MSVC 下链接时报一堆LNK2038,提示_ITERATOR_DEBUG_LEVEL不匹配,或者RuntimeLibrary不匹配,整个链接过程直接终止。

原因:当前工程编译选项用的是/MDd(Debug 模式动态运行时),而你链的预编译库是 Release 模式编出来的,用的是/MD。MSVC 对运行时库匹配检查得很严,Debug/Release 混用就会触发。

解决:检查你选的库文件是不是debug目录下的那一份。大多数预编译包会区分quazip.lib和quazipd.lib,Debug 工程必须链quazipd.lib。如果确实没有 debug 版本,可以在当前工程的预处理定义里加上_ITERATOR_DEBUG_LEVEL=0,但这样做只建议临时验证,不适合长期交付。

5.2 场景二:运行时 0xc000007b,程序无法正常启动

现象:程序构建成功,双击 exe 弹出“应用程序无法正常启动 0xc000007b”,点击确定后退出。

原因:这个错误码的含义是“无效映像格式”,最常见的是 32 位/64 位混用。比如你编译的是 64 位程序,但quazip.dll是 32 位;或者quazip.dll是 64 位,而它依赖的zlib1.dll是 32 位。

解决:用 Dependency Walker 或 dumpbin 查看quazip.dll和zlib1.dll的位数,确保三者一致。也可以用最笨的办法:手动检查预编译包目录名,x64 的库专门放 x64 目录,缺文件时优先从同目录补齐。

5.3 场景三:exe 报“无法定位程序输入点于动态链接库”

现象:程序运行时报“无法定位程序输入点 xxx 于动态链接库 quazip.dll”,启动即失败。

原因:链接期用的导入库和运行期加载的 DLL 不是同一个文件。常见情况是你链接的是quazip.lib,但quazip.dll是另一个版本生成的,两者导出表对不上。

解决:确认工程用的IMPORTED_IMPLIB和IMPORTED_LOCATION指向同一个构建产物目录。我一般会把quazip.lib和quazip.dll放在同一个bin、同一个lib子目录里,这样链接器和运行时的引用路径天然一致,不会出现两个版本同时在工程里的尴尬。

5.4 场景四:Qt platform plugin 加载失败,QuaZip 背了锅

现象:程序编译链接全部正常,发布到新机器后运行,报This application failed to start because no Qt platform plugin could be initialized。

原因:这不是 QuaZip 的问题,而是整个 Qt 运行环境没部署完整。你在开发机上可能因为系统 PATH 指向了 Qt 安装目录而一切正常,但发布目录里缺少 Qt 平台插件qwindows.dll,程序启动时 Qt GUI 模块无法初始化。

解决:在构建输出目录跑一遍windeployqt app.exe。它会把 Qt 依赖、平台插件、运行库全部复制到位。之后再把quazip.dll和zlib1.dll手工补进同一个目录。习惯上我把这个步骤做到构建脚本里,每次编译完自动执行。

5.5 场景五:压缩包里的中文文件名变成乱码

现象:用 QuaZip 压缩的文件在 Windows 资源管理器或第三方解压工具里打开,中文名显示为乱码;或者反过来,别人打的 zip 包里中文名在你的程序里读出来是乱的。

原因:zip 标准早期用 CP437 编码文件名,后来中文环境常用 GBK 或 UTF-8。QuaZip 默认按本地编码处理,不设置字符集就会产生不一致。

解决:写入端统一设置zip.setFileNameCodec("UTF-8"),读取端解压前也必须调用同样方法,确保两边用的是同一套字符集。如果你要兼容老式 Windows 工具,可能得改用GBK,这点要根据业务场景定,没有万能解。重要的是:写入和读取必须用相同的 codec,否则必乱。

6. 更进一步:用 QBuffer 在内存里生成 Zip 再快速校验

很多场景下,zip 包不需要真的落盘。比如你要把一批配置文本传给某个接口,或者把内存中的多段日志打成压缩包再上传,直接写临时文件再删,既慢又容易留垃圾。QuaZip 的构造函数支持传入QIODevice*,这意味着用QBuffer就能在内存里完成读写。

QByteArray zipBytes; QBuffer buffer(&zipBytes); buffer.open(QIODevice::ReadWrite); { QuaZip zip(&buffer); if (!zip.open(QuaZip::mdCreate)) { return false; } QuaZipFile file(&zip); file.open(QIODevice::WriteOnly, QuaZipNewInfo(QStringLiteral("config.txt"))); file.write("key=value\n"); file.close(); zip.close(); } buffer.close(); // 验证:把刚生成的内存数据当 zip 重新读一遍 QBuffer checkBuffer(&zipBytes); checkBuffer.open(QIODevice::ReadOnly); QuaZip zipIn(&checkBuffer); zipIn.open(QuaZip::mdUnzip); zipIn.goToFirstFile(); QuaZipFile checkFile(&zipIn); checkFile.open(QIODevice::ReadOnly); qDebug() << checkFile.readAll();

说明:大括号的作用是控制QuaZip对象的生命周期,确保它在buffer关闭前析构,把最后的 zip 尾记录写完。QuaZip zip(&buffer)直接接管 QBuffer 作为输出流,所有 zip 数据最终落在zipBytes这个QByteArray里。验证环节把zipBytes重新包装成只读 QBuffer,再按mdUnzip模式打开读取,确认文档结构和内容都没问题。

内存 zip 做法省去文件 IO,但要注意一点:如果zipBytes特别大,内存占用和文件方式没有本质区别。对于 10 MB 以内的轻量数据,这是很好的方案;超过 100 MB 还是老老实实走磁盘,避免一次性内存分配把程序拖垮。

从那以后,我每次接预编译库都会强制走一遍:先用qmake -v记下 Qt 版本,再对着 include/lib/bin 里的文件名核对编译器与位数,最后在 Release 模式下用windeployqt清一遍依赖,确认quazip.dll和zlib1.dll都在 exe 旁边。这套流程听着啰嗦,却让我躲过了好几次深夜翻车的局面。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询