资讯动态

QuaZip编译产物使用指南:Qt项目集成与避坑实践

发布时间:2026/10/20 4:03:56 来源:尧图企业网站定制
简介这份资源是面向Windows平台C开发者的QuaZip预编译库包省去自行编译开源压缩库的繁琐流程可直接集成到项目中处理ZIP与RAR档案。压缩包共29个文件约427KB包含17个头文件、4个lib静态库、2个dll动态库以及cmake配置与pkgconfig描述文件覆盖Qt5版本下quazip1-qt5的调试与发布两套产物。借助这些文件开发者可快速实现ZIP档案的打开与创建、文件读写、内容遍历、增删条目及基础RAR读取并利用其异常与错误码机制排查问题。配置时只需将头文件目录加入包含路径把lib加入链接器附加依赖项并保证dll位于可执行文件同目录或PATH中即可。目前已有599人学习下载适合需要快速落地压缩功能的C项目参考使用。1. 拿到 QuaZip 编译产物之后一个 C 工程师的库文件落地笔记如果你正在用 Qt 写桌面端又恰好需要读写 zip 包——比如做安装包解压、日志归档、资源打包——那你大概率绕不开 QuaZip 这个库。它基于 zlib 和 minizip 封装接口是 Qt 风格的和 QFile、QIODevice 那套东西能无缝对接。但问题来了官方源码只给 .cpp 和 .h你得自己编译。而编译 QuaZip 这件事在 Windows 上尤其折腾——zlib 的版本要对、Qt 的 kit 要匹配、CMake 的配置项要选对稍不留神就是一堆链接错误。所以一份「已经编译好的 lib 和 dll」对很多 C 开发者来说省掉的不只是时间更是反复试错的精力。这篇笔记就围绕这份编译产物把怎么用、怎么配、坑在哪一次讲清楚。2. QuaZip 编译产物的组成与选型逻辑lib、dll 和头文件怎么对应2.1 先搞清楚你拿到的是什么静态库、动态库与导入库的区别一份 QuaZip 的编译产物通常包含三类文件头文件.h、库文件.lib和动态库.dll。但这里的 .lib 在不同编译方式下含义完全不同这是第一个容易翻车的地方。如果你拿到的是静态库版本那 .lib 就是完整的机器码链接时直接嵌进你的 exe运行时不需要额外的 dll。这种方式的优点是部署简单缺点是 exe 体积会变大而且如果多个模块都用 QuaZip每个模块都会嵌一份浪费空间。如果你拿到的是动态库版本那 .lib 只是导入库import library里面只有符号索引真正的实现在 .dll 里。链接时 .lib 告诉链接器「这些函数在某个 dll 里」运行时系统再去加载那个 dll。这种方式 exe 小但部署时必须把 dll 一起带上否则运行时报「找不到 QuaZip.dll」。还有一种情况是同时提供了静态库和动态库两套产物你需要根据自己的场景选。我一般会这样判断如果是内部工具、单机软件直接用静态库省事如果是插件架构、多个模块共享或者需要热更新库文件那就用动态库。注意静态库和动态库的 .lib 不能混用。如果你把静态库的 .lib 当成导入库去链接动态库的 dll会出现「无法解析的外部符号」或者运行时崩溃。2.2 版本匹配Qt kit、编译器、架构三者必须对齐QuaZip 本身不依赖 Qt 的 GUI 模块但它依赖 QtCore。这意味着你的项目用的 Qt 版本、编译器版本、目标架构必须和编译 QuaZip 时用的完全一致。差一个 minor 版本都可能出问题。具体来说你需要确认这几个信息维度必须匹配的内容不匹配的后果Qt 版本大版本号和小版本号链接时报 QtCore 符号缺失编译器MSVC 版本或 MinGW 版本ABI 不兼容运行时崩溃架构x86 还是 x64链接器直接报错无法识别库格式构建类型Debug 还是 ReleaseDebug 库链接 Release 库会报错举个例子如果你的项目是 MSVC 2019 Qt 5.15.2 x64 Release那 QuaZip 的编译产物也必须是这个组合。如果你拿到的是 MinGW 编译的那 MSVC 项目根本用不了因为两者的 name mangling 规则不同。常见做法是在拿到编译产物之前先在自己的开发机上跑一段代码打印出 Qt 版本和编译器信息#include QtCore/QtGlobal #include iostream int main() { std::cout Qt version: QT_VERSION_STR std::endl; std::cout Compiler: QT_COMPILER_VERSION_STR std::endl; // 架构信息可以通过 sizeof(void*) 判断 std::cout Pointer size: sizeof(void*) * 8 bits std::endl; return 0; }这段代码输出的是你当前项目的编译环境。拿着这个信息去比对 QuaZip 产物的编译环境一致了再用不一致就趁早换。2.3 目录结构建议把编译产物放进项目里的正确姿势很多人拿到 lib 和 dll 之后随手一放结果项目换台机器就编译不过。我建议在项目根目录下建一个third_party/quazip目录按下面的结构组织third_party/ └── quazip/ ├── include/ │ └── quazip/ │ ├── QuaZip │ ├── QuaZipFile │ ├── JlCompress │ └── ... ├── lib/ │ ├── x64/ │ │ ├── Debug/ │ │ │ └── quazip.lib │ │ └── Release/ │ │ └── quazip.lib │ └── x86/ │ └── ... └── bin/ ├── x64/ │ ├── Debug/ │ │ └── quazip.dll │ └── Release/ │ └── quazip.dll └── x86/ └── ...这样组织的好处是不同架构、不同构建类型的产物互不干扰CMake 或 qmake 里可以用变量拼接路径换平台时只需要改一个变量。在 qmake 的 .pro 文件里可以这样写# 根据当前构建配置自动选择库路径 CONFIG(debug, debug|release) { BUILD_TYPE Debug } else { BUILD_TYPE Release } # 根据目标架构选择 contains(QT_ARCH, x86_64) { ARCH_DIR x64 } else { ARCH_DIR x86 } QUAZIP_ROOT $$PWD/third_party/quazip INCLUDEPATH $$QUAZIP_ROOT/include LIBS -L$$QUAZIP_ROOT/lib/$$ARCH_DIR/$$BUILD_TYPE -lquazip # 如果是动态库还需要把 dll 拷贝到输出目录 # 这一步可以用 QMAKE_POST_LINK 做这段 qmake 配置的核心逻辑是用CONFIG(debug, debug|release)判断当前是 Debug 还是 Release用QT_ARCH判断目标架构然后拼出对应的库路径。INCLUDEPATH指向头文件目录LIBS指向库文件目录并指定库名。参数说明-L后面跟库文件所在目录-l后面跟库名不带前缀 lib 和后缀 .lib/.a。在 Windows 上qmake 会自动处理 .lib 后缀在 Linux 上会处理 .a 或 .so。如果你用的是 CMake配置方式类似# 根据构建类型选择库目录 if(CMAKE_BUILD_TYPE STREQUAL Debug) set(QUAZIP_BUILD_TYPE Debug) else() set(QUAZIP_BUILD_TYPE Release) endif() # 根据目标架构选择 if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(QUAZIP_ARCH x64) else() set(QUAZIP_ARCH x86) endif() set(QUAZIP_ROOT ${CMAKE_SOURCE_DIR}/third_party/quazip) target_include_directories(your_target PRIVATE ${QUAZIP_ROOT}/include) target_link_libraries(your_target PRIVATE ${QUAZIP_ROOT}/lib/${QUAZIP_ARCH}/${QUAZIP_BUILD_TYPE}/quazip.lib )CMake 的写法更显式一些直接把完整路径传给target_link_libraries。注意CMAKE_SIZEOF_VOID_P在 64 位系统上是 832 位是 4用这个来判断架构比读环境变量更可靠。3. 在 Qt 项目中集成 QuaZip从配置到第一个压缩包3.1 用 QuaZip 创建一个 zip 包最小可运行示例配置好库路径之后先别急着往大项目里塞写一个最小示例验证链接和运行都正常。下面这段代码创建一个 zip 包往里写两个文件然后关闭。#include QuaZip #include QuaZipFile #include QDebug int main() { // 创建 zip 文件参数是文件路径 QuaZip zip(test_archive.zip); // 以写入模式打开如果是已有文件会被覆盖 if (!zip.open(QuaZip::mdCreate)) { qDebug() 无法创建 zip 文件: zip.getZipError(); return -1; } // 往 zip 里添加第一个文件 QuaZipFile file1(zip); if (!file1.open(QIODevice::WriteOnly, QuaZipNewInfo(hello.txt))) { qDebug() 无法创建 hello.txt: zip.getZipError(); return -1; } file1.write(Hello, QuaZip!); file1.close(); // 添加第二个文件放在子目录里 QuaZipFile file2(zip); QuaZipNewInfo info(data/config.json); // 设置压缩级别0-99 最高 info.setCompressionLevel(9); if (!file2.open(QIODevice::WriteOnly, info)) { qDebug() 无法创建 config.json: zip.getZipError(); return -1; } file2.write({\key\: \value\}); file2.close(); // 关闭 zip 文件这一步会写入中央目录 zip.close(); qDebug() zip 文件创建完成; return 0; }这段代码的逻辑说明QuaZip对象代表一个 zip 文件open(mdCreate)表示以创建模式打开。QuaZipFile代表 zip 内的一个条目open时传入QuaZipNewInfo指定条目名称。写入完成后必须调用close()否则数据不会刷到磁盘。最后zip.close()会写入 zip 的中央目录这是 zip 格式的关键结构没有它文件无法被正常识别。参数说明QuaZip::mdCreate是打开模式其他模式还有mdRead只读、mdWrite读写已有文件、mdAppend追加。QuaZipNewInfo的构造函数接受条目名称还可以设置时间戳、权限等。setCompressionLevel控制压缩级别0 是不压缩9 是最大压缩默认是 6。编译这个示例时如果用的是动态库需要确保 quazip.dll 在可执行文件同级目录或系统 PATH 里。如果运行时提示「找不到 quazip.dll」把 dll 拷贝到 exe 旁边即可。3.2 读取和提取 zip 包内容遍历条目与解压创建验证通过之后再验证读取。下面这段代码打开一个 zip 包遍历所有条目把内容打印出来。#include QuaZip #include QuaZipFile #include QDebug void listZipContents(const QString zipPath) { QuaZip zip(zipPath); // 以只读模式打开 if (!zip.open(QuaZip::mdRead)) { qDebug() 无法打开 zip: zip.getZipError(); return; } // 获取条目总数 int entryCount zip.getEntriesCount(); qDebug() 条目总数: entryCount; // 遍历所有条目 for (bool more zip.goToFirstFile(); more; more zip.goToNextFile()) { QuaZipFileInfo info; zip.getCurrentFileInfo(info); qDebug() 文件名: info.name 压缩后大小: info.compressedSize 原始大小: info.uncompressedSize CRC: info.crc; // 读取文件内容 QuaZipFile file(zip); if (file.open(QIODevice::ReadOnly)) { QByteArray content file.readAll(); qDebug() 内容: content; file.close(); } else { qDebug() 无法读取: info.name; } } zip.close(); }逻辑说明goToFirstFile()和goToNextFile()是遍历条目的标准方式前者返回 false 表示 zip 为空后者返回 false 表示没有更多条目。getCurrentFileInfo()获取当前条目的元信息包括文件名、压缩前后大小、CRC 校验值等。读取内容时QuaZipFile需要重新 open不能直接复用遍历时的状态。参数说明QuaZipFileInfo的name是条目在 zip 内的相对路径compressedSize是压缩后字节数uncompressedSize是原始字节数。如果两个值相等说明这个条目没有压缩可能是已经压缩过的格式比如 jpg。crc是循环冗余校验值可以用来验证解压后的数据是否完整。注意如果 zip 包里有中文文件名需要确认 QuaZip 编译时是否启用了 UTF-8 支持。老版本的 QuaZip 默认用本地编码跨平台时会出现乱码。常见做法是统一用英文文件名或者在读取时手动转换编码。3.3 用 JlCompress 做快速压缩解压一行代码的便利与限制QuaZip 还提供了一个便利类JlCompress封装了常见的压缩解压操作。如果你不需要精细控制用这个类可以少写很多代码。#include JlCompress #include QDebug // 压缩单个文件到 zip bool ok JlCompress::compressFile(archive.zip, input.txt); if (!ok) { qDebug() 压缩失败; } // 压缩整个目录 ok JlCompress::compressDir(archive.zip, my_folder); if (!ok) { qDebug() 目录压缩失败; } // 解压整个 zip 到目录 ok JlCompress::extractDir(archive.zip, output_folder); if (!ok) { qDebug() 解压失败; } // 只解压其中一个文件 ok JlCompress::extractFile(archive.zip, hello.txt, output.txt); if (!ok) { qDebug() 单文件解压失败; }逻辑说明compressFile把单个文件压进 zipcompressDir递归压缩整个目录extractDir把 zip 解压到指定目录extractFile只提取指定条目。这些函数返回 bool成功为 true失败为 false。参数说明compressDir的第二个参数是源目录路径zip 内的条目名会保留相对路径结构。extractDir的第二个参数是目标目录如果目录不存在会自动创建。extractFile的第三个参数是输出文件路径如果只想拿到内容不想写文件可以用extractFile的重载版本返回 QByteArray。JlCompress 的限制在于它不支持自定义压缩级别不支持设置密码不支持流式读写大文件。如果你的场景需要这些还是得回到 QuaZip 和 QuaZipFile 的原始接口。4. 避坑指南QuaZip 编译产物使用中的五个典型问题4.1 链接时报「无法解析的外部符号」现象编译通过链接时出现大量unresolved external symbol符号名里带QuaZip或JlCompress。原因最常见的是库文件路径没配对或者 .lib 和 .dll 的架构不一致。还有一种可能是静态库和动态库混用了——比如项目里链接的是静态库的 .lib但实际期望的是动态库的导入库。解决先确认LIBS或target_link_libraries里的路径指向正确的 .lib。然后用dumpbin /headers quazip.libMSVC或nm -C libquazip.aMinGW查看库的架构信息。如果是动态库确认 .lib 是导入库而不是静态库。最后检查项目里有没有同时链接多个版本的 QuaZip。4.2 运行时提示「找不到 quazip.dll」现象编译链接都通过双击 exe 或运行时弹窗提示缺少 dll。原因动态库版本的 QuaZip 需要 dll 在运行时可见。Windows 搜索 dll 的顺序是exe 所在目录 → 系统目录 → PATH 环境变量。如果 dll 不在这些位置就会报错。解决把 quazip.dll 拷贝到 exe 同级目录。如果项目有多个构建配置建议在构建后自动拷贝。qmake 里可以用QMAKE_POST_LINKCMake 里可以用add_custom_command。另外注意 Debug 和 Release 的 dll 不能混用Debug 版 dll 依赖 Debug 版 Qt5Cored.dllRelease 版依赖 Release 版。4.3 压缩中文文件名出现乱码现象压缩包在 Windows 上打开正常在 Linux 或 macOS 上文件名乱码或者反过来。原因zip 格式对文件名编码没有统一规定。Windows 常用 GBKLinux 常用 UTF-8。QuaZip 默认使用本地编码跨平台时就会出问题。解决统一用英文文件名是最省事的做法。如果必须用中文可以在编译 QuaZip 时启用 UTF-8 支持或者在代码里手动转换编码。QuaZipNewInfo 的构造函数可以传入已经转好编码的字符串。读取时用QTextCodec做转换。不过更推荐的做法是在 zip 内部统一用 UTF-8显示时再转成本地编码。4.4 大文件压缩时内存暴涨现象压缩几百 MB 的文件时程序内存占用飙升甚至被系统杀掉。原因QuaZipFile 的readAll()和write()会把整个文件内容读进内存。如果文件很大内存自然扛不住。解决改用流式读写分块处理。下面是一个分块压缩的示例QuaZipFile zipFile(zip); zipFile.open(QIODevice::WriteOnly, QuaZipNewInfo(large_file.bin)); QFile sourceFile(large_source.bin); sourceFile.open(QIODevice::ReadOnly); const int CHUNK_SIZE 1024 * 1024; // 1MB while (!sourceFile.atEnd()) { QByteArray chunk sourceFile.read(CHUNK_SIZE); zipFile.write(chunk); } sourceFile.close(); zipFile.close();这段代码每次只读 1MB写进 zip 后再读下一块。内存占用稳定在 1MB 左右不会随文件大小增长。参数CHUNK_SIZE可以根据实际情况调整一般 1MB 到 4MB 比较合适。4.5 Debug 和 Release 版本混用导致崩溃现象Debug 模式下运行正常Release 模式下崩溃或者反过来。原因Debug 和 Release 的 CRTC 运行时库不同内存分配和释放的规则也不同。如果在一个模块里分配内存在另一个模块里释放而两个模块的构建类型不一致就会堆损坏。解决确保项目、QuaZip 库、Qt 库三者的构建类型完全一致。Debug 项目链接 Debug 版 QuaZip 和 Debug 版 QtRelease 同理。如果 QuaZip 只提供了 Release 版那你的项目也只能用 Release 模式链接它。不要试图在 Debug 项目里链接 Release 库即使链接通过运行时也可能出问题。5. 进阶技巧用 QuaZip 做增量更新与完整性校验5.1 增量更新只解压变化的文件很多桌面软件需要增量更新只下载变化的文件而不是整个安装包。用 QuaZip 可以实现这个逻辑先读取 zip 内的文件列表和 CRC和本地已安装的文件对比只解压 CRC 不同的条目。#include QuaZip #include QuaZipFile #include QFile #include QCryptographicHash // 计算本地文件的 CRC32 quint32 calculateCrc32(const QString filePath) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { return 0; } QByteArray data file.readAll(); // 这里用 zlib 的 crc32 函数QuaZip 内部也用的这个 // 实际项目中可以链接 zlib 直接调用 return crc32(0, reinterpret_castconst Bytef*(data.constData()), data.size()); } void incrementalExtract(const QString zipPath, const QString targetDir) { QuaZip zip(zipPath); if (!zip.open(QuaZip::mdRead)) { return; } for (bool more zip.goToFirstFile(); more; more zip.goToNextFile()) { QuaZipFileInfo info; zip.getCurrentFileInfo(info); QString localPath targetDir / info.name; quint32 localCrc calculateCrc32(localPath); // CRC 相同说明文件没变跳过 if (localCrc info.crc) { continue; } // CRC 不同解压覆盖 QuaZipFile file(zip); if (file.open(QIODevice::ReadOnly)) { QFile outFile(localPath); // 确保目录存在 QDir().mkpath(QFileInfo(localPath).absolutePath()); if (outFile.open(QIODevice::WriteOnly)) { outFile.write(file.readAll()); outFile.close(); } file.close(); } } zip.close(); }逻辑说明遍历 zip 内所有条目对每个条目计算本地对应文件的 CRC32。如果 CRC 相同说明文件内容没变跳过如果不同或本地文件不存在则解压覆盖。这样只需要传输一个 zip 包客户端只解压变化的文件节省 IO 和更新时间。参数说明crc32函数来自 zlibQuaZip 内部也用它做校验。QDir().mkpath确保目标目录存在避免写入失败。实际项目中CRC 计算可以优化——不需要读整个文件可以分块计算减少内存占用。5.2 完整性校验用 CRC 验证解压结果zip 格式的每个条目都存了 CRC32 值解压后可以重新计算 CRC 做比对验证数据是否完整。这在网络传输场景下特别有用——如果下载的 zip 包损坏解压出来的文件 CRC 会对不上。bool verifyZipEntry(QuaZip zip, const QuaZipFileInfo info) { QuaZipFile file(zip); if (!file.open(QIODevice::ReadOnly)) { return false; } QByteArray content file.readAll(); file.close(); // 计算实际内容的 CRC quint32 actualCrc crc32(0, reinterpret_castconst Bytef*(content.constData()), content.size()); // 和 zip 里记录的 CRC 比对 return actualCrc info.crc; }逻辑说明读取条目内容后用同样的 CRC32 算法重新计算和info.crc比对。如果一致说明数据完整不一致说明 zip 包损坏或解压过程出错。参数说明crc32的第一个参数是初始值传 0 即可第二个参数是数据指针第三个参数是数据长度。注意content.size()返回的是 int如果文件超过 2GB 需要用quint64并分块计算。我一般会在解压完成后跑一遍校验尤其是从网络下载的 zip 包。虽然多花一点时间但能避免「解压出来一堆损坏文件却不知道」的尴尬。从那以后每次做自动更新我都会在解压后强制走一遍 CRC 校验确认所有条目都匹配才标记更新成功。希望这些经验帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑