You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用libzip打开嵌套压缩包(Zip内的Zip)

问题根源与解决方案

你遇到的核心问题是完全误解了zip_file_tzip_t的本质区别——这俩是完全不同的结构体,根本不能强制转换:

  • zip_t代表的是一个完整的zip归档文件(可以是磁盘上的文件,也可以是内存中的数据),是整个压缩包的句柄
  • zip_file_t只是这个归档里单个文件的读取流,它只能用来读取该文件的内容,本身不具备zip归档的解析能力

直接把zip_file_t强转成zip_t,相当于让程序把一段文件流的指针当成完整的归档结构体来用,必然会触发段错误。


正确的处理流程

要读取嵌套压缩包,你需要先把内层的child.zip文件内容读取出来(放到内存或临时文件),再用libzip的API把这段内容当成新的zip归档打开,之后才能读取它内部的子文件。

示例代码(内存读取方式)

这种方式不需要创建临时文件,适合小体积的嵌套压缩包:

#include "zip.h"
#include "gtk.h"

int main() {
    int error;
    zip_t *mainzipfile = zip_open(g_file_get_path(file), ZIP_CHECKCONS, &error);
    if (!mainzipfile) {
        // 处理主压缩包打开错误
        zip_error_t err;
        zip_error_init_with_code(&err, error);
        g_print("Failed to open main zip: %s\n", zip_error_strerror(&err));
        zip_error_fini(&err);
        return 1;
    }

    // 1. 获取child.zip的文件信息(主要是大小)
    zip_stat_t stat;
    if (zip_stat(mainzipfile, "child.zip", 0, &stat) != 0) {
        g_print("Failed to stat child.zip\n");
        zip_close(mainzipfile);
        return 1;
    }

    // 2. 分配内存并读取child.zip的内容
    void *child_zip_data = malloc(stat.size);
    if (!child_zip_data) {
        g_print("Out of memory\n");
        zip_close(mainzipfile);
        return 1;
    }

    zip_file_t *childzip = zip_fopen(mainzipfile, "child.zip", ZIP_RDONLY);
    if (!childzip) {
        g_print("Failed to open child.zip in main archive\n");
        free(child_zip_data);
        zip_close(mainzipfile);
        return 1;
    }

    // 读取完整的child.zip内容到内存
    zip_int64_t bytes_read = zip_fread(childzip, child_zip_data, stat.size);
    if (bytes_read != stat.size) {
        g_print("Failed to read full child.zip content\n");
        zip_fclose(childzip);
        free(child_zip_data);
        zip_close(mainzipfile);
        return 1;
    }
    zip_fclose(childzip);

    // 3. 用内存中的数据打开新的zip归档
    zip_t *child_zip = zip_open_from_buffer(child_zip_data, stat.size, 0, ZIP_CHECKCONS, &error);
    if (!child_zip) {
        zip_error_t err;
        zip_error_init_with_code(&err, error);
        g_print("Failed to open child zip from buffer: %s\n", zip_error_strerror(&err));
        zip_error_fini(&err);
        free(child_zip_data);
        zip_close(mainzipfile);
        return 1;
    }

    // 4. 现在可以读取child.zip里的子文件了(比如索引1的文件)
    zip_file_t *childofchild = zip_fopen_index(child_zip, 1, ZIP_RDONLY);
    if (childofchild) {
        // 处理子文件内容,比如读取、解析等
        zip_fclose(childofchild);
    } else {
        g_print("Failed to open child of child\n");
    }

    // 5. 清理资源:注意zip_open_from_buffer默认会接管内存,所以不需要手动free
    zip_close(child_zip);
    zip_close(mainzipfile);

    return 0;
}

关键注意事项

  • zip_open_from_buffer默认会接管传入的内存,所以调用zip_close(child_zip)时会自动释放child_zip_data,不需要手动free;如果不想让libzip接管内存,可以传入ZIP_MEMORY_NOCOPY标志,但之后需要自己负责释放。
  • 如果嵌套压缩包体积很大,建议用临时文件的方式(把child.zip写入到临时文件,再用zip_open打开临时文件路径),避免占用过多内存。
  • 一定要做好每一步的错误检查,libzip的API几乎都会返回错误状态,忽略它们很容易导致崩溃或资源泄漏。

内容的提问来源于stack exchange,提问作者Bret Joseph

火山引擎 最新活动