使用libzip压缩文件和文件夹
简单说说自己遇到的坑:
分清楚三个组件:zlib、minizip和libzip。zlib是底层和最基础的C库,用于使用Deflate算法压缩和解压缩文件流或者单个文件,但是如果要压缩文件夹就很麻烦,主要是不知道如何归档,在zip内部形成对应的目录。这时就需要用更高级别的库,也就是minizip或libzip。
minizip、libzip随着版本迭代接口一直变化,我连续使用了通义千问、文心一言、gemini三个AI,基本上没给出能使用的代码,主要是函数接口总是不对,或者参数多了或者少了。像这种情况就不要再参考AI给出的答案了,赶紧翻官方文档才是正经。
minizip和libzip都是基于zlib实现的,都尝试使用过,感觉还是libzip的接口设计更清晰一点,官方文档说明也还不错。
压缩文件夹的功能需要借助于操作文件系统的库来组织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,有以下几点值得注意:
- libzip压缩的zip内部的文件名默认采用UTF-8编码。
- libzip要求使用正斜杠 ('/') 作为目录分隔符。
- libzip操作不同的zip线程安全,操作同一个zip线程不安全。
- zip_source_buffer这个函数的接口的第四个参数如果非0,会自动托管申请的资源。官方文档提到需要保证传入zip_source_buffer的数据资源需要保证跟zip_source_t一样的声明周期,但是笔者经过测试,正确的行为应该是传入zip_source_buffer的数据资源需要保证调用zip_close之前都有效,否则就有问题。
使用libzip压缩文件和文件夹的更多相关文章
- 【C#公共帮助类】WinRarHelper帮助类,实现文件或文件夹压缩和解压,实战干货
关于本文档的说明 本文档使用WinRAR方式来进行简单的压缩和解压动作,纯干货,实际项目这种压缩方式用的少一点,一般我会使用第三方的压缩dll来实现,就如同我上一个压缩类博客,压缩的是zip文件htt ...
- C#文件或文件夹压缩和解压方法(通过ICSharpCode.SharpZipLib.dll)
我在网上收集一下文件的压缩和解压的方法,是通过ICSharpCode.SharpZipLib.dll 来实现的 一.介绍的目录 第一步:下载压缩和解压的 ICSharpCode.SharpZipLib ...
- 关于SharpZipLib压缩分散的文件及整理文件夹的方法
今天为了解决压缩分散的文件时,发现想通过压缩对象直接进行文件夹整理很麻烦,因为SharpZipLib没有提供压缩进某个指定文件夹的功能,在反复分析了SharpZipLib提供的各个接口方法后,终于找到 ...
- Python(文件、文件夹压缩处理模块,shelve持久化模块,xml处理模块、ConfigParser文档配置模块、hashlib加密模块,subprocess系统交互模块 log模块)
OS模块 提供对操作系统进行调用的接口 os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作目 ...
- C#压缩、解压缩文件(夹)(rar、zip)
主要是使用Rar.exe压缩解压文件(夹)(*.rar),另外还有使用SevenZipSharp.dll.zLib1.dll.7z.dll压缩解压文件(夹)(*.zip).需要注意的几点如下: 1.注 ...
- 使用ICSharpZipLib将文件夹压缩为zip文件
序言: 在我接触Git和SVN之前,我最常用的保存数据的办法就是把文件夹压缩成一个zip文件,添加上时间戳.下面是我在学习C#的文件操作之后做的一个练习,使用开源的ICSharpZipLib来 ...
- java.util.zip压缩打包文件总结一:压缩文件及文件下面的文件夹
一.简述 zip用于压缩和解压文件.使用到的类有:ZipEntry ZipOutputStream 二.具体实现代码 package com.joyplus.test; import java.io ...
- java压缩指定目录下的所有文件和文件夹的代码
将代码过程较好的代码段备份一下,下边资料是关于java压缩指定目录下的所有文件和文件夹的代码,希望对码农有帮助. String sourceDir="E:\test";int pa ...
- java压缩文件或文件夹并导出
java压缩文件或文件夹并导出 tozipUtil: package com.zhl.push.Utils; import java.io.File; import java.io.FileInput ...
- 【转载】.NET压缩/解压文件/夹组件
转自:http://www.cnblogs.com/asxinyu/archive/2013/03/05/2943696.html 阅读目录 1.前言 2.关于压缩格式和算法的基础 3.几种常见的.N ...
随机推荐
- 干货 springcloud之 poenFeign的使用
PoenFeign集成到springcloud项目中 先创建一个springboot项目 这里就不多说了 application.yml文件: server: port: 8082spring: ap ...
- docker lnmp配置
1.lnmp网络与目录规划 172.16.10.0/24 nginx:172.16.10.10 mysql:172.16.10.20 php:172.16.10.30 网站访问主目录:/wwwroot ...
- vue3+vant 引入Dialog Toast都会失败报错not defined
今天在封装vant组件的时候,刚好要用到toast提示信息的组件,索性就按照官网提供的引入方法进行正常的引入,嘿,好家伙,一顿操作下来后发现竟然报Toast未定义,这就纳闷了,明明步骤都是对的啊,所以 ...
- C# 配置文件增加自定义节点
话不多说直接开撸! 首先创建一个Config的文件夹然后新增一个后缀名为.config的文件 配置文件的代码如下: <?xml version="1.0" encoding= ...
- boss直聘__zp_stoken__逆向
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 目标网站 aHR0cHM6 ...
- 关于 CSDN 的恶臭嘴脸
我有段时间确实希望通过 CSDN 增加我文章的阅读量.但是我怎么使用 CSDN,我想要的大概也就 cnblogs 相对符合预期,CSDN 真是垃圾. 为了钱真是什么都不要了,让我们这种没有自主经济来源 ...
- kettle从入门到精通 第十五课 kettle 映射 (子转换)01
1.kettle 里面的映射和java代码里面的封装是一个概念,就是将一个可复用的模块单独抽离为公共模块供其他模块引用,用到的步骤或者组件如下 2.构建子映射,子映射需要用到映射输入规范和映射输出规范 ...
- base64图片文件上传OSS,下载OSS图片转换为InputStream,文件转base64,base64转文件工具类
base64图片文件上传OSS,下载OSS图片转换为InputStream,文件转base64,base64转文件工具类 OSSUtils.java public class OSSUtils { p ...
- linux查看端口命令 lsof netstat
[root@VM-4-3-centos /]# lsof -i:8881COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEnode 15526 roo ...
- 一文了解Spark引擎的优势及应用场景
Spark引擎诞生的背景 Spark的发展历程可以追溯到2009年,由加州大学伯克利分校的AMPLab研究团队发起.成为Apache软件基金会的孵化项目后,于2012年发布了第一个稳定版本. 以下是S ...