• 使用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 
    
    #include 
    #include 
    #include 
    
    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之前都有效,否则就有问题。
  • 相关阅读:
    Idea工具中,使用Mapper对象有红线
    气传导耳机有哪些品牌?性能不错的气传导耳机分享
    Oracle高级
    深入理解Python协程:从基础到实战
    vue-element-admin+springboot登录功能实现
    2023年数维杯国际大学生数学建模挑战赛A题
    ​软考-高级-系统架构设计师教程(清华第2版)【第10章 软件架构的演化和维护(P345~382)-思维导图】​
    str(n)cpy的注意事项以及memset的简单使用
    2020华数杯全国大学生数学建模竞赛B题-基于混合模拟退火算法的三维零件的切割模型与计算(附MATLAB代码)
    数字菜场:智慧农贸大屏可视化大数据管理系统
  • 原文地址:https://www.cnblogs.com/charlee44/p/18299531