简单说说自己遇到的坑:

  1. 分清楚三个组件:zlib、minizip和libzip。zlib是底层和最基础的C库,用于使用Deflate算法压缩和解压缩文件流或者单个文件,但是如果要压缩文件夹就很麻烦,主要是不知道如何归档,在zip内部形成对应的目录。这时就需要用更高级别的库,也就是minizip或libzip。

  2. minizip、libzip随着版本迭代接口一直变化,我连续使用了通义千问、文心一言、gemini三个AI,基本上没给出能使用的代码,主要是函数接口总是不对,或者参数多了或者少了。像这种情况就不要再参考AI给出的答案了,赶紧翻官方文档才是正经。

  3. minizip和libzip都是基于zlib实现的,都尝试使用过,感觉还是libzip的接口设计更清晰一点,官方文档说明也还不错。

  4. 压缩文件夹的功能需要借助于操作文件系统的库来组织zip内部的归档目录,我这里使用的是C++17的std::filesystem。

具体代码实现如下:

#include <zip.h>

#include <filesystem>
#include <fstream>
#include <iostream> using namespace std; void CompressFile2Zip(std::filesystem::path unZipFilePath,
const char* relativeName, zip_t* zipArchive) {
std::ifstream file(unZipFilePath, std::ios::binary);
file.seekg(0, std::ios::end);
size_t bufferSize = file.tellg();
char* bufferData = (char*)malloc(bufferSize); file.seekg(0, std::ios::beg);
file.read(bufferData, bufferSize); //第四个参数如果非0,会自动托管申请的资源,直到zip_close之前自动销毁。
zip_source_t* source =
zip_source_buffer(zipArchive, bufferData, bufferSize, 1); if (source) {
if (zip_file_add(zipArchive, relativeName, source, ZIP_FL_OVERWRITE) < 0) {
std::cerr << "Failed to add file " << unZipFilePath
<< " to zip: " << zip_strerror(zipArchive) << std::endl;
zip_source_free(source);
}
} else {
std::cerr << "Failed to create zip source for " << unZipFilePath << ": "
<< zip_strerror(zipArchive) << std::endl;
}
} void CompressFile(std::filesystem::path unZipFilePath,
std::filesystem::path zipFilePath) {
int errorCode = 0;
zip_t* zipArchive = zip_open(zipFilePath.generic_u8string().c_str(),
ZIP_CREATE | ZIP_TRUNCATE, &errorCode);
if (zipArchive) {
CompressFile2Zip(unZipFilePath, unZipFilePath.filename().string().c_str(),
zipArchive); errorCode = zip_close(zipArchive);
if (errorCode != 0) {
zip_error_t zipError;
zip_error_init_with_code(&zipError, errorCode);
std::cerr << zip_error_strerror(&zipError) << std::endl;
zip_error_fini(&zipError);
}
} else {
zip_error_t zipError;
zip_error_init_with_code(&zipError, errorCode);
std::cerr << "Failed to open output file " << zipFilePath << ": "
<< zip_error_strerror(&zipError) << std::endl;
zip_error_fini(&zipError);
}
} void CompressDirectory2Zip(std::filesystem::path rootDirectoryPath,
std::filesystem::path directoryPath,
zip_t* zipArchive) {
if (rootDirectoryPath != directoryPath) {
if (zip_dir_add(zipArchive,
std::filesystem::relative(directoryPath, rootDirectoryPath)
.generic_u8string()
.c_str(),
ZIP_FL_ENC_UTF_8) < 0) {
std::cerr << "Failed to add directory " << directoryPath
<< " to zip: " << zip_strerror(zipArchive) << std::endl;
}
} for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) {
if (entry.is_regular_file()) {
CompressFile2Zip(
entry.path().generic_u8string(),
std::filesystem::relative(entry.path(), rootDirectoryPath)
.generic_u8string()
.c_str(),
zipArchive);
} else if (entry.is_directory()) {
CompressDirectory2Zip(rootDirectoryPath, entry.path().generic_u8string(),
zipArchive);
}
}
} void CompressDirectory(std::filesystem::path directoryPath,
std::filesystem::path zipFilePath) {
int errorCode = 0;
zip_t* zipArchive = zip_open(zipFilePath.generic_u8string().c_str(),
ZIP_CREATE | ZIP_TRUNCATE, &errorCode);
if (zipArchive) {
CompressDirectory2Zip(directoryPath, directoryPath, zipArchive); errorCode = zip_close(zipArchive);
if (errorCode != 0) {
zip_error_t zipError;
zip_error_init_with_code(&zipError, errorCode);
std::cerr << zip_error_strerror(&zipError) << std::endl;
zip_error_fini(&zipError);
}
} else {
zip_error_t zipError;
zip_error_init_with_code(&zipError, errorCode);
std::cerr << "Failed to open output file " << zipFilePath << ": "
<< zip_error_strerror(&zipError) << std::endl;
zip_error_fini(&zipError);
}
} int main() {
//压缩文件
//CompressFile("C:/Data/Builder/Demo/view.tmp", "C:/Data/Builder/Demo/view.zip"); //压缩文件夹
CompressDirectory("C:/Data/Builder/Demo", "C:/Data/Builder/Demo.zip"); return 0;
}

关于使用的libzip,有以下几点值得注意:

  1. libzip压缩的zip内部的文件名默认采用UTF-8编码。
  2. libzip要求使用正斜杠 ('/') 作为目录分隔符。
  3. libzip操作不同的zip线程安全,操作同一个zip线程不安全。
  4. zip_source_buffer这个函数的接口的第四个参数如果非0,会自动托管申请的资源。官方文档提到需要保证传入zip_source_buffer的数据资源需要保证跟zip_source_t一样的声明周期,但是笔者经过测试,正确的行为应该是传入zip_source_buffer的数据资源需要保证调用zip_close之前都有效,否则就有问题。

使用libzip压缩文件和文件夹的更多相关文章

  1. 【C#公共帮助类】WinRarHelper帮助类,实现文件或文件夹压缩和解压,实战干货

    关于本文档的说明 本文档使用WinRAR方式来进行简单的压缩和解压动作,纯干货,实际项目这种压缩方式用的少一点,一般我会使用第三方的压缩dll来实现,就如同我上一个压缩类博客,压缩的是zip文件htt ...

  2. C#文件或文件夹压缩和解压方法(通过ICSharpCode.SharpZipLib.dll)

    我在网上收集一下文件的压缩和解压的方法,是通过ICSharpCode.SharpZipLib.dll 来实现的 一.介绍的目录 第一步:下载压缩和解压的 ICSharpCode.SharpZipLib ...

  3. 关于SharpZipLib压缩分散的文件及整理文件夹的方法

    今天为了解决压缩分散的文件时,发现想通过压缩对象直接进行文件夹整理很麻烦,因为SharpZipLib没有提供压缩进某个指定文件夹的功能,在反复分析了SharpZipLib提供的各个接口方法后,终于找到 ...

  4. Python(文件、文件夹压缩处理模块,shelve持久化模块,xml处理模块、ConfigParser文档配置模块、hashlib加密模块,subprocess系统交互模块 log模块)

    OS模块 提供对操作系统进行调用的接口 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname")  改变当前脚本工作目 ...

  5. C#压缩、解压缩文件(夹)(rar、zip)

    主要是使用Rar.exe压缩解压文件(夹)(*.rar),另外还有使用SevenZipSharp.dll.zLib1.dll.7z.dll压缩解压文件(夹)(*.zip).需要注意的几点如下: 1.注 ...

  6. 使用ICSharpZipLib将文件夹压缩为zip文件

    序言:     在我接触Git和SVN之前,我最常用的保存数据的办法就是把文件夹压缩成一个zip文件,添加上时间戳.下面是我在学习C#的文件操作之后做的一个练习,使用开源的ICSharpZipLib来 ...

  7. java.util.zip压缩打包文件总结一:压缩文件及文件下面的文件夹

    一.简述 zip用于压缩和解压文件.使用到的类有:ZipEntry  ZipOutputStream 二.具体实现代码 package com.joyplus.test; import java.io ...

  8. java压缩指定目录下的所有文件和文件夹的代码

    将代码过程较好的代码段备份一下,下边资料是关于java压缩指定目录下的所有文件和文件夹的代码,希望对码农有帮助. String sourceDir="E:\test";int pa ...

  9. java压缩文件或文件夹并导出

    java压缩文件或文件夹并导出 tozipUtil: package com.zhl.push.Utils; import java.io.File; import java.io.FileInput ...

  10. 【转载】.NET压缩/解压文件/夹组件

    转自:http://www.cnblogs.com/asxinyu/archive/2013/03/05/2943696.html 阅读目录 1.前言 2.关于压缩格式和算法的基础 3.几种常见的.N ...

随机推荐

  1. Git 合并冲突

    目录 文章目录 目录 合并冲突 合并分支 合并最近的两次提交 合并冲突 步骤: git status 找到冲突的文件 将冲突去除 将冲突文件重新加入临时区 git add egis/api/v1/ro ...

  2. ef.core 事务不回滚的我遇到的一种情况分享

    比如有几个Repository:_storeRep,_inventoryRep,_storeItemRep.基类封装有BeginTransaction():   using(var trans=_st ...

  3. golang 怎么获取kubernetes deployments的状态?

    如果我们需要把k8s的信息展示为一个友好的web页面.那么deployment 的信息基本上是非常重要的(大部分的服务都是使用deployment部署). 从yaml 中我们能获取到很多关于deplo ...

  4. Android 12(S) Binder(三)

    学以致用,这一节来native binder实战! android 12中的service用到的Bp.Bn文件多由aidl生成,所以实战中也用aidl来生成. 1.文件目录结构 文件目录结构如上,偷懒 ...

  5. Hangfire 使用笔记 任务可以分离到别的项目中,无需重复部署Hangfire,通过API方式通信。

    "巨人们"的地址 Hangfire Mysql: https://github.com/arnoldasgudas/Hangfire.MySqlStorage 在获取set表数据的 ...

  6. AppFlow上新——智谱ChatGLM轻松接入聊天

    智谱AI 开放平台提供一系列具有不同功能和定价的大模型,包括通用大模型.超拟人大模型.图像大模型.向量大模型等,并且支持使用您的私有数据对模型进行微调.其中ChatGLM系列模型在国内也享有盛名,现在 ...

  7. harbor 构建企业级镜像仓库

    安装harbor 构建企业级镜像仓库 Harbor是由VMware公司开源的镜像仓库,harbor是在docker Registry上进行了企业级扩展,从而获得了更广泛的应用,这些新的企业级特性包括: ...

  8. RTOS入门

    RTOS是什么? RTOS是一款操作系统,相当于Windows\Linux 分为ucos FreeRTOS RT-Thread LiteOS 比裸机开发的优势在于,多任务系统,不必串行 临界区 临界区 ...

  9. GIT文件上传演示

    Be Written By Handat.憨大头 注:以下内容默认你已经做好了git工具的用户账户配置. (1)创建Gitee线上代码仓库,HTTPS协议地址就是仓库地址,如例https://gite ...

  10. margin的用法 清除默认样式 display属性值 块状元素 内联元素 行内块元素

    margin的用法: 1,margin是在元素的宽高以外的 2,作用:控制元素之间的位置关系 3,margin不能改变盒子本身大小的 4,单一一个方向设置margin值: margin-left    ...