使用libzip压缩文件和文件夹

简介: 使用libzip压缩文件和文件夹

使用libzip压缩文件和文件夹

简单说说自己遇到的坑:

  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;
}
CPP 折叠 复制 全屏

关于使用的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之前都有效,否则就有问题。

分类: C++ , 基础组件

标签: C/C++ , 压缩 , libzip

相关文章
|
4月前
|
Linux 编译器
目录文件篇
目录文件篇
|
9月前
|
算法 编译器 API
C++ MiniZip实现目录压缩与解压
Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法。它最初由`Jean-Loup Gailly`和`Mark Adler`开发,旨在成为一个高效、轻量级的压缩库,其被广泛应用于许多领域,包括网络通信、文件压缩、数据库系统等。其压缩算法是基于`DEFLATE`算法,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。在Zlib项目中的`contrib`目录下有一个`minizip`子项目,minizip实际上不是`zlib`库的一部分,而是一个独立的开源库,用于处理ZIP压缩文件格式。它提供了对ZIP文件的创建和解压的简单接口。minizip在很多情况下与`zlib`一起使用
289 0
C++ MiniZip实现目录压缩与解压
|
10月前
|
Java
Java实现zip文件压缩:单个文件、文件夹以及文件和文件夹的压缩
Java实现zip文件压缩:单个文件、文件夹以及文件和文件夹的压缩
|
Linux
解压 .solitairetheme8 文件
解压 .solitairetheme8 文件
92 0
C#编程-116:获取指定目录下的所有文件及文件夹
C#编程-116:获取指定目录下的所有文件及文件夹
569 0
|
JavaScript Java
【Auto.js】[zip压缩] 将文件夹压缩成zip包
【Auto.js】[zip压缩] 将文件夹压缩成zip包
403 0
|
算法 C++
使用VC++压缩解压缩文件夹
前言  项目中要用到一个压缩解压缩的模块, 看了很多文章和源代码, 都不是很称心, 现在把我自己实现的代码和大家分享. 要求:          1.使用Unicode(支持中文).        2.
1048 0