使用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
相关产品推荐
相关产品推荐

