使用 C++ 和 minizip 实现 ZIP 压缩解压工具

news/2025/10/11 14:56:18/文章来源:https://www.cnblogs.com/oloroso/p/19135049

在软件开发中,文件压缩和解压是常见的需求。今天我们来介绍一个基于 minizip 库的 C++ ZIP 工具类 - ZipUtility,它可以轻松地处理 ZIP 文件的创建和解压。

这篇文章使用AI辅助编写。

核心功能

1. 压缩功能

ZipUtility::zipArchive 方法可以将多个文件打包成一个 ZIP 压缩包:

std::vector<std::string> files = {"document.txt", "image.jpg"};
ZipUtility::zipArchive(files, "archive.zip");

支持的特性:

  • 多种压缩方法:存储(不压缩)、DEFLATE、BZIP2
  • 压缩级别控制:从最快速度到最佳压缩比
  • 密码保护:使用密码加密压缩文件
  • ZIP64 支持:处理大文件(超过 4GB)
  • 全局注释:为 ZIP 文件添加描述信息

2. 解压功能

ZipUtility::unzipArchive 方法可以解压 ZIP 文件:

ZipUtility::unzipArchive("archive.zip", "output_folder");

支持的特性:

  • 目录结构恢复:可选择是否保留原始目录结构
  • 密码解压:支持加密 ZIP 文件的解压
  • 自动目录创建:递归创建所需的目录结构

设计亮点

跨平台兼容

代码通过条件编译实现了 Windows 和 Unix-like 系统的兼容:

#ifdef _WIN32#define SEPARATOR '\\'
#else#define SEPARATOR '/'
#endif

完善的错误处理

定义了详细的错误码枚举,涵盖各种可能的问题场景:

enum class ZipResult
{OK,                    // 操作成功ERR_OPEN_ZIP,          // 打开ZIP文件失败ERR_CREATE_ZIP,        // 创建ZIP文件失败ERR_OPEN_FILE,         // 打开源文件失败// ... 更多错误码
};

递归目录创建

createDirectory 方法能够递归创建多级目录,确保解压路径的正确性。

使用示例

基本压缩

std::vector<std::string> files = {"file1.txt", "file2.jpg"};
auto result = ZipUtility::zipArchive(files, "backup.zip",ZipUtility::CompressionMethod::DEFLATE,ZipUtility::CompressionLevel::BEST_COMPRESSION
);

加密压缩

auto result = ZipUtility::zipArchive(files, "secure.zip",ZipUtility::CompressionMethod::DEFLATE,ZipUtility::CompressionLevel::DEFAULT,"mypassword"  // 设置密码
);

解压文件

// 保留目录结构
auto result = ZipUtility::unzipArchive("archive.zip", "extracted", nullptr, true);// 仅提取文件(不保留目录结构)
auto result = ZipUtility::unzipArchive("archive.zip", "extracted", nullptr, false);

总结

这个 ZipUtility 类提供了一个简洁而强大的接口来处理 ZIP 文件操作。它的设计考虑了实际使用中的各种需求,包括跨平台兼容性、错误处理和功能灵活性。无论是用于文件备份、数据分发还是应用程序的资源管理,都是一个很好的工具选择。

代码结构清晰,错误处理完善,非常适合集成到现有的 C++ 项目中。如果你正在寻找一个轻量级的 ZIP 处理解决方案,这个工具类值得一试!

完整代码

ZipUtility.h

#include <string>
#include <vector>class ZipUtility
{
public:// 操作结果枚举enum class ZipResult{OK,                     // 操作成功ERR_OPEN_ZIP,           // 打开ZIP文件失败ERR_CREATE_ZIP,         // 创建ZIP文件失败ERR_OPEN_FILE,          // 打开源文件失败ERR_READ_FILE,          // 读取文件失败ERR_WRITE_FILE,         // 写入文件失败ERR_CLOSE_ZIP,          // 关闭ZIP文件失败ERR_CREATE_DIR,         // 创建目录失败ERR_INVALID_ZIP,        // 无效的ZIP文件ERR_INVALID_PASSWORD,   // 密码错误ERR_CRC,                // CRC校验失败ERR_UNSUPPORTED,        // 不支持的格式ERR_UNKNOWN             // 未知错误};// 压缩方法枚举enum class CompressionMethod{STORE   = 0,   // 不压缩DEFLATE = 8,   // Z_DEFLATED DEFLATE压缩BZIP2   = 12   // Z_BZIP2ED BZIP2压缩(如果编译支持)};// 压缩级别枚举enum class CompressionLevel{DEFAULT          = -1,   // Z_DEFAULT_COMPRESSION,BEST_SPEED       = 1,    // Z_BEST_SPEED,BEST_COMPRESSION = 9,    // Z_BEST_COMPRESSION,NO_COMPRESSION   = 0     // Z_NO_COMPRESSION};/*** 创建 ZIP 压缩文件** @param filelist       需要压缩的文件路径列表* @param zipPath        输出的 ZIP 文件路径* @param method         压缩方法 (默认为DEFLATE)* @param level          压缩级别 (默认为DEFAULT)* @param password       加密密码 (默认为空)* @param zip64          是否启用ZIP64格式(大文件支持) (默认为true)* @param globalComment  ZIP文件的全局注释 (默认为空)* @return               操作状态*/static ZipResult zipArchive(const std::vector<std::string>& filelist,const std::string&              zipPath,CompressionMethod               method = CompressionMethod::DEFLATE,CompressionLevel                level  = CompressionLevel::DEFAULT,const char* password = nullptr, bool zip64 = true,const char* globalComment = nullptr);/*** 解压 ZIP 文件** @param zipPath        输入的 ZIP 文件路径* @param outDirectory   解压输出目录* @param password       解压密码 (默认为空)* @param restorePath    是否保留目录结构 (默认为true)* @return               操作状态*/static ZipResult unzipArchive(const std::string& zipPath, const std::string& outDirectory,const char* password = nullptr, bool restorePath = true);private:// 创建目录(递归)static ZipResult createDirectory(const std::string& path);// 转换ZIP操作错误码static ZipResult convertZipError(int err);// 转换UNZIP操作错误码static ZipResult convertUnzipError(int err);
};

ZipUtility.cpp

#include "ZipUtility.h"#include "minizip/ioapi.h"
#include "minizip/unzip.h"
#include "minizip/zip.h"#include <algorithm>
#include <cstring>
#include <ctime>
#include <string>
#include <sys/stat.h>
#include <vector>#ifdef _WIN32
#    include <direct.h>
#    include <windows.h>
#    define mkdir(path, mode) _mkdir(path)
#    define SEPARATOR '\\'
#else
#    include <dirent.h>
#    include <unistd.h>
#    define SEPARATOR '/'
#endifZipUtility::ZipResult ZipUtility::zipArchive(const std::vector<std::string>& filelist,const std::string& zipPath, CompressionMethod method,CompressionLevel level, const char* password,bool zip64, const char* globalComment)
{// 打开 ZIP 文件zipFile zf = zipOpen(zipPath.c_str(), APPEND_STATUS_CREATE);if (!zf) {return ZipResult::ERR_CREATE_ZIP;}int  err = ZIP_OK;char buf[8192];for (const auto& file : filelist) {// 打开本地文件FILE* fin = fopen(file.c_str(), "rb");if (!fin) {zipClose(zf, globalComment);return ZipResult::ERR_OPEN_FILE;}// 获取文件信息zip_fileinfo zi;memset(&zi, 0, sizeof(zi));time_t     current  = time(nullptr);struct tm* tm       = localtime(&current);zi.tmz_date.tm_sec  = tm->tm_sec;zi.tmz_date.tm_min  = tm->tm_min;zi.tmz_date.tm_hour = tm->tm_hour;zi.tmz_date.tm_mday = tm->tm_mday;zi.tmz_date.tm_mon  = tm->tm_mon;zi.tmz_date.tm_year = tm->tm_year + 1900;zi.dosDate          = static_cast<uLong>(current);// 提取文件名(不含路径)std::string filename = file;size_t      pos      = file.find_last_of("\\/");if (pos != std::string::npos) {filename = file.substr(pos + 1);}// 在ZIP中打开新文件int zip64flag = zip64 ? 1 : 0;err           = zipOpenNewFileInZip3_64(zf,filename.c_str(),&zi,nullptr,0,   // extrafield_localnullptr,0,         // extrafield_globalnullptr,   // commentstatic_cast<int>(method),static_cast<int>(level),0,               // raw-MAX_WBITS,      // windowBitsDEF_MEM_LEVEL,   // memLevelZ_DEFAULT_STRATEGY,password,0,   // crcForCryptingzip64flag);if (err != ZIP_OK) {fclose(fin);zipClose(zf, globalComment);return convertZipError(err);}// 读取并写入文件内容size_t read = 0;while ((read = fread(buf, 1, sizeof(buf), fin)) > 0) {err = zipWriteInFileInZip(zf, buf, static_cast<unsigned>(read));if (err != ZIP_OK) {fclose(fin);zipCloseFileInZip(zf);zipClose(zf, globalComment);return convertZipError(err);}}// 关闭ZIP中的文件err = zipCloseFileInZip(zf);if (err != ZIP_OK) {fclose(fin);zipClose(zf, globalComment);return convertZipError(err);}fclose(fin);}// 关闭ZIP文件err = zipClose(zf, globalComment);return convertZipError(err);
}ZipUtility::ZipResult ZipUtility::unzipArchive(const std::string& zipPath,const std::string& outDirectory,const char* password, bool restorePath)
{// 打开 ZIP 文件unzFile uf = unzOpen(zipPath.c_str());if (!uf) return ZipResult::ERR_OPEN_ZIP;// 创建输出目录createDirectory(outDirectory);// 获取ZIP文件信息unz_global_info64 gi;if (unzGetGlobalInfo64(uf, &gi) != UNZ_OK) {unzClose(uf);return ZipResult::ERR_INVALID_ZIP;}int  err = UNZ_OK;char filename_inzip[256];char buf[8192];// 遍历ZIP内所有文件for (uLong i = 0; i < gi.number_entry; i++) {unz_file_info64 file_info;err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip), nullptr, 0, nullptr, 0);if (err != UNZ_OK) {unzClose(uf);return convertUnzipError(err);}// 处理文件路径std::string fullPath = outDirectory;if (restorePath) {fullPath += SEPARATOR + std::string(filename_inzip);}else {// 仅提取文件名std::string fileName(filename_inzip);size_t      pos = fileName.find_last_of("\\/");if (pos != std::string::npos) {fileName = fileName.substr(pos + 1);}fullPath += SEPARATOR + fileName;}// 创建目录size_t pos = fullPath.find_last_of("\\/");if (pos != std::string::npos) {ZipResult dirResult = createDirectory(fullPath.substr(0, pos));if (dirResult != ZipResult::OK) {unzClose(uf);return dirResult;}}// 打开ZIP中的文件if (password) {err = unzOpenCurrentFilePassword(uf, password);}else {err = unzOpenCurrentFile(uf);}if (err != UNZ_OK) {unzClose(uf);return convertUnzipError(err);}// 创建输出文件FILE* fout = fopen(fullPath.c_str(), "wb");if (!fout) {unzCloseCurrentFile(uf);unzClose(uf);return ZipResult::ERR_WRITE_FILE;}// 解压文件内容do {err = unzReadCurrentFile(uf, buf, sizeof(buf));if (err < 0) break;if (err > 0) {if (fwrite(buf, 1, err, fout) != static_cast<size_t>(err)) {err = UNZ_ERRNO;break;}}} while (err > 0);fclose(fout);int closeErr = unzCloseCurrentFile(uf);// 处理CRC错误if (err == UNZ_CRCERROR || closeErr == UNZ_CRCERROR) {remove(fullPath.c_str());unzClose(uf);return ZipResult::ERR_CRC;}if (err != UNZ_OK && err != UNZ_EOF) {unzClose(uf);return convertUnzipError(err);}// 移动到下一个文件if (i < gi.number_entry - 1) {err = unzGoToNextFile(uf);if (err != UNZ_OK) {unzClose(uf);return convertUnzipError(err);}}}unzClose(uf);return ZipResult::OK;
}ZipUtility::ZipResult ZipUtility::createDirectory(const std::string& path)
{if (path.empty()) return ZipResult::OK;#ifdef _WIN32if (CreateDirectoryA(path.c_str(), NULL) || GetLastError() == ERROR_ALREADY_EXISTS) {return ZipResult::OK;}
#elsestruct stat st;if (stat(path.c_str(), &st) == 0) {if (S_ISDIR(st.st_mode)) return ZipResult::OK;return ZipResult::ERR_CREATE_DIR;}size_t pos = 0;do {pos                = path.find_first_of("\\/", pos + 1);std::string subdir = path.substr(0, pos);if (mkdir(subdir.c_str(), 0755) != 0 && errno != EEXIST) {return ZipResult::ERR_CREATE_DIR;}} while (pos != std::string::npos);
#endifreturn ZipResult::OK;
}ZipUtility::ZipResult ZipUtility::convertZipError(int err)
{switch (err) {case ZIP_OK: return ZipResult::OK;case ZIP_ERRNO: return ZipResult::ERR_OPEN_FILE;case ZIP_PARAMERROR: return ZipResult::ERR_INVALID_ZIP;case ZIP_BADZIPFILE: return ZipResult::ERR_INVALID_ZIP;case ZIP_INTERNALERROR: return ZipResult::ERR_WRITE_FILE;default: return ZipResult::ERR_UNKNOWN;}
}ZipUtility::ZipResult ZipUtility::convertUnzipError(int err)
{switch (err) {case UNZ_OK: return ZipResult::OK;case UNZ_ERRNO: return ZipResult::ERR_READ_FILE;case UNZ_PARAMERROR: return ZipResult::ERR_INVALID_ZIP;case UNZ_BADZIPFILE: return ZipResult::ERR_INVALID_ZIP;case UNZ_INTERNALERROR: return ZipResult::ERR_UNKNOWN;case UNZ_CRCERROR: return ZipResult::ERR_CRC;// case UNZ_BADPASSWORD: return ZipResult::ERR_INVALID_PASSWORD;default: return ZipResult::ERR_UNKNOWN;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/934810.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

西部数码使用外部dns服务器怎么配置解析

西部数码使用外部dns服务器怎么配置解析在西部数码购买的域名如果使用的dns是外部的,要在该外部dns管理平台去设置解析 比如使用的阿里云的dns如何设置解析 在阿里云dns管理页面找到dns粘贴到西部数码该域名的解析地址…

一看就懂,Oracle认证体系中的OCP中级认证

对于刚接触Oracle数据库或者打算往数据库领域发展的人来说,需要了解一下这个认证:Oracle的中级认证OCP认证专家。 OCP认证全称为Oracle Certified Professional(甲骨文认证专家),是全球知名的IT技术认证体系之一,由…

实用指南:SDN 控制器深度剖析:架构、对比与实践部署

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025 年试验机生产厂家最新推荐榜单:聚焦优质企业,助力精准选购高低温等各类试验设备弹簧拉压/弹簧疲劳/高频弹簧疲劳/U型弹簧专用试验机厂家推荐

在工业生产与科研检测的关键环节,试验机对保障产品质量、验证材料性能起着不可替代的作用。随着市场需求激增,试验机生产厂家数量骤增,但行业乱象也随之显现,产品质量参差不齐、技术水平差距悬殊、服务体系不完善等…

IIS/如何查看IIS上部署网站的实时连接数

我们在IIS发布的Web网站,如何查看网站实时的连接数呢? 1、首先打开运行框,输入perfmon.msc 2、打开监视工具-->性能监视器 3、点击 “+”号,添加计数项 Web Service/Current Connections 4、可以查看到网站的实…

Supabase:无需后端代码的 Web 开发完整解决方案

在现代 Web 应用开发中,构建 MVP(最简可行产品)时,后端基础设施的搭建常常成为主要瓶颈。开发者需要处理数据库、API、用户认证、权限控制、文件存储等多个模块,开发成本较高。 Supabase 提供了一种替代方案:它以…

Halo RAG!

Halo AI 助手插件最新版本深度集成了 RAG 问答功能,支持将 Halo 文章或 Docsme 文档作为知识库,为访客提供智能问答服务。用户需先创建知识库,配置嵌入模型并选择内容范围以建立索引;随后创建问答应用,关联知识库…

拼叨叨砍价系统:实体店低成本引流的营销利器

在互联网营销深入各行业的当下,“砍一刀” 凭借强社交裂变属性,已成为大众熟知的营销模式。拼叨叨砍价系统基于这一模式,针对实体店商家营销痛点,打造出轻量化、易操作的砍价活动模块,支持微信公众号部署,能帮助…

2025 自动门生产厂家最新推荐榜:权威筛选优质品牌,含选购指南与实力厂家深度解析

在出入口自动化浪潮下,自动门已成为商业综合体、医院、工业厂区等场景的刚需设备,但其市场呈现 “繁荣与乱象并存” 的态势。部分品牌缺乏核心技术,产品故障率高、寿命短,无形中增加使用成本;售后体系缺失导致故障…

grafana-使用grafana-image-renderer:v4.0.17渲染仪表盘图像

grafana-使用grafana-image-renderer:v4.0.17渲染仪表盘图像v4.0.17需要grafana版本在11.3.8以上。我的是centos7, 升级grafana很简单,备份一下数据和配置,然后停掉grafana服务,然后使用【rpm -Uvh 新版本的rpm包 …

医德出诊排班挂号管理系统:医院高效运营与便民服务的智能解决方案

在数字化医疗浪潮下,传统挂号模式的低效、信息不透明等问题日益凸显,患者就医体验不佳,医院管理压力也持续增加。医德出诊排班挂号系统作为一款聚焦医疗服务场景的数字化工具,依托微信公众号生态,整合出诊排班、预…

一佳教育培训课程系统小程序:一站式教育数字化解决方案

在数字化教育浪潮下,培训机构对高效、多功能的线上教学管理工具需求迫切。一佳教育培训课程系统小程序应运而生,涵盖微信小程序版本,支持微擎系统交付,集课程管理、学员运营、营销推广、商城功能于一体,以加密源码…

SLS Copilot 实践:基于 SLS 灵活构建 LLM 应用的数据基础设施

本文将分享我们在构建 SLS SQL Copilot 过程中的工程实践,展示如何基于阿里云 SLS 打造一套完整的 LLM 应用数据基础设施。作者:顾汉杰(执少)"纸上得来终觉浅,绝知此事要躬行。" —— 陆游在 LLM 应用快…

2025 木饰面源头厂家最新推荐榜单:21 年标杆企业领衔,背景墙/全屋 /格栅/碳晶板全品类最新推荐及选购指南

木饰面市场正面临性能适配难、供货周期长、品质参差不齐的行业痛点:家装中潮湿区域需防水板材、高端空间看重环保平整度,公装对防火抗菌要求严苛,而大小订单交付失衡问题普遍。同时,近年新锐品牌涌现与技术迭代,让…

2025 年北京市清理化粪池公司最新推荐排行榜:聚焦高压技术与全城服务的权威甄选朝阳区/丰台区/海淀区/通州区清理化粪池厂家推荐

在城市化持续推进的当下,化粪池清理作为维护城市环境卫生、保障管道系统通畅的核心环节,其服务质量直接关系到居民生活舒适度与企业运营效率。然而当前行业乱象频发,部分企业设备陈旧导致清理不彻底,不仅残留污垢易…

报表方案Stimulsoft 2025.4 重磅发布!新增AI报表助手、C#脚本支持、全新图表类型等多项功能!

全球知名的报表解决方案服务商 Stimulsoft 正式发布2025.4 版本!该版本面向报表、仪表盘与可填写PDF表单的开发者,带来了多项令人期待的新特性,包括 Reports.JS 中运行 C# 脚本的支持、AI 智能报表助手、全新3D图表…

Prometheus的Exporter的数据采集机制

Prometheus的Exporter的数据采集机制Prometheus的Exporter采集器通过一套精巧的架构实现数据采集与暴露,而其个性化定制能力则得益于开放的客户端库和灵活的设计模式。下面我们深入解析其工作原理和定制方法。 🔄 E…

2025 年珠三角 / 中山 / 东莞 / 佛山厂房出售公司推荐:中创集团产业生态型厂房的价值与服务解析

在广东省 “坚持实体经济为本、制造业当家” 的发展导向下,《广东省加快扩大工业有效投资实施方案(2025—2027 年)》明确提出要打造高水平产业承载平台,聚焦新材料、生物医药、先进制造等新赛道培育主导产业。当前…

CTFshow-web方向(更新中)

萌新杯 intval与preg_match绕过方法: 进制绕过二进制或者八进制或者十六进制等等 双重取反~~绕过有时候需要编码需要绕过的字符$a=~("system"); echo urlencode($a);换行\n绕过 编码绕过SQL查询中,如果我们…

拷贝和上传文件,涉及隐私协议

如果使用拷贝和复制粘贴功能,以及使用从微信上传文件的功能,需要在weixin后台完善隐私协议。wx.setClipboardData({duration: 2000,title: 成功,data: name,icon: success,}); 拷贝 错误提示: setClipboardData:fai…