使用inflate(,Z_BLOCK)检测deflate块边界时日志输出异常的排查
问题分析与解决方案
核心问题:对state->last的误解
你搞混了state->last的作用——它标记的是整个deflate压缩流的最后一个块,而非单个deflate块的边界。在zlib的deflate格式里,每个块头部确实有一个1位的"last block"标志,但state->last变量只会在处理到整个压缩流的最终块时被置位。这就是为什么解压289MB文件只看到3次日志,大概率是WinZip把文件分成了3个独立的deflate流块。
正确检测每个deflate块边界的方法
要在每个块边界触发日志,得调整检测逻辑,有两种可行方式:
1. 修改zlib源码,在块头部解析阶段检测
找到zlib中处理块类型的TYPE模式代码段(这里是解析每个块头部标志的地方),直接检查当前块的"last block"位:
case TYPE: // ... 原有解析块类型的代码 ... // 检查当前块的last标志(state->flags的最低位) if (state->flags & 1) { fprintf(stderr, "Fernando: %s() 处理到一个块(当前是该段的最后一块)\n", __func__); } else { fprintf(stderr, "Fernando: %s() 处理到一个块边界\n", __func__); } // ... 后续代码 ...
这里的state->flags & 1对应单个deflate块头部的last标志位,每个块都会触发一次判断。
2. 在上层调用代码(unzip.c)中判断
因为你用了Z_BLOCK参数调用inflate(),函数会在每个块处理完成后返回Z_OK,同时strm->data_type的第6位(对应数值64)会被置位,表示到达块边界。可以直接在unzReadCurrentFile()的调用逻辑里加日志:
int ret = inflate(strm, Z_BLOCK); if (ret == Z_OK && (strm->data_type & 64)) { fprintf(stderr, "到达deflate块边界\n"); }
这种方式不用修改zlib源码,更易维护。
续解压功能的实现提示
要做跨重启的续解压,核心是记录两个关键信息:
- 压缩文件中当前处理到的输入偏移位置
- zlib的
z_stream完整状态(包括next_in、avail_in、total_in、total_out,以及内部state结构体的必要字段)
重启时,先把压缩文件的读取指针恢复到记录的偏移,重新初始化z_stream并加载保存的状态,然后继续调用inflate()即可。另外要注意,WinZip生成的压缩包可能包含多个deflate流,需要额外处理流之间的切换逻辑。
内容的提问来源于stack exchange,提问作者Anton Fernando
相关产品推荐
相关产品推荐

