You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用libzip Android端口向Zip归档写入数据的回调实现问题

解决libzip Android端口zip_add生成空文件的问题

我来帮你搞定这个libzip回调的坑——你遇到的空文件问题,核心就是zip_source_callback没有正确响应libzip的命令并提供文件数据。libzip会给回调发送一系列命令来获取文件元数据、读取内容、清理资源,咱们一步步写对这个回调:

第一步:定义回调的状态结构体

首先要准备一个状态结构体,用来在回调之间传递关键数据(比如要写入的内容、当前读取位置):

typedef struct {
    const char *file_content; // 要写入zip的内容(如果是本地文件可以存文件描述符)
    size_t content_len;       // 内容总长度
    size_t current_pos;       // 当前读取的偏移位置
} ZipSourceState;

第二步:实现完整的回调函数

必须处理libzip发送的关键命令,少一个都可能导致空文件:

#include <string.h>
#include <stdlib.h>
#include <zip.h>

ssize_t _source_callback(void *state, void *data, size_t len, enum zip_source_cmd cmd) {
    ZipSourceState *ctx = (ZipSourceState *)state;
    ssize_t r = -1;

    switch (cmd) {
        case ZIP_SOURCE_OPEN:
            // 每次打开资源时重置读取位置
            ctx->current_pos = 0;
            r = 0; // 返回0表示成功
            break;

        case ZIP_SOURCE_READ:
            // 计算本次能读取的字节数:不能超过剩余内容,也不能超过请求长度
            size_t bytes_to_read = (len < ctx->content_len - ctx->current_pos) ? len : (ctx->content_len - ctx->current_pos);
            if (bytes_to_read > 0) {
                memcpy(data, ctx->file_content + ctx->current_pos, bytes_to_read);
                ctx->current_pos += bytes_to_read;
                r = bytes_to_read; // 返回实际读取的字节数
            } else {
                r = 0; // 返回0表示读取完毕(EOF)
            }
            break;

        case ZIP_SOURCE_CLOSE:
            // 如果是动态分配的内容,这里可以做清理(比如free(ctx->file_content))
            r = 0;
            break;

        case ZIP_SOURCE_STAT: {
            // 敲黑板:必须提供文件大小,否则libzip会认为文件是空的!
            zip_stat_t *stat = (zip_stat_t *)data;
            memset(stat, 0, sizeof(*stat));
            stat->size = ctx->content_len; // 设置文件的实际大小
            stat->valid = ZIP_STAT_SIZE; // 告诉libzip我们提供了size字段
            r = 0;
            break;
        }

        case ZIP_SOURCE_ERROR: {
            // 处理错误,默认返回OK即可(如果有具体错误可以设置对应码)
            int *error = (int *)data;
            error[0] = ZIP_ER_OK;
            error[1] = 0;
            r = 0;
            break;
        }

        case ZIP_SOURCE_FREE:
            // 释放我们的状态结构体(如果是malloc分配的)
            free(ctx);
            r = 0;
            break;

        default:
            // 未知命令返回-1
            r = -1;
            break;
    }

    return r;
}

第三步:正确调用zip_add

现在用这个回调来创建并添加文件到zip归档:

// 示例:把字符串写入zip中的"test.txt"文件
int add_file_to_zip(zip_t *zip) {
    const char *content = "Hello LibZip on Android!";
    size_t content_len = strlen(content);

    // 分配并初始化状态结构体
    ZipSourceState *state = malloc(sizeof(ZipSourceState));
    if (!state) return -1;
    state->file_content = content;
    state->content_len = content_len;
    state->current_pos = 0;

    // 创建zip source
    zip_source_t *source = zip_source_callback(zip, state, _source_callback);
    if (!source) {
        free(state);
        return -1;
    }

    // 添加文件到归档
    int ret = zip_add(zip, "test.txt", source);
    if (ret < 0) {
        zip_source_free(source);
        return -1;
    }

    // 最后一定要关闭zip归档,确保所有数据写入磁盘
    // 注意:zip_close会自动释放source,不需要手动调用zip_source_free
    return zip_close(zip);
}

关键注意事项

  • 必须处理ZIP_SOURCE_STAT:这是很多人忽略的点,如果不提供文件大小,libzip无法判断要读取多少数据,直接生成空文件
  • ZIP_SOURCE_READ的返回值:返回实际读取的字节数,返回0表示EOF,返回-1表示错误
  • 资源清理:如果状态结构体或内容是动态分配的,一定要在ZIP_SOURCE_FREE或ZIP_SOURCE_CLOSE中释放,避免内存泄漏
  • 本地文件适配:如果要添加的是Android本地文件,把状态结构体里的file_content换成文件描述符,在ZIP_SOURCE_OPEN时打开文件,ZIP_SOURCE_READ时调用read()读取数据,ZIP_SOURCE_CLOSE时关闭文件即可

内容的提问来源于stack exchange,提问作者Rahul Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:27:00