Python gzip模块多进程写入产生尾字节导致解压失败如何解决?
问题解决方案
问题根源
你遇到的报错本质是现有实现逻辑的问题,并非gzip模块bug:
- 你调用的
gzip.compress()每次都会生成包含完整gzip头、压缩内容、gzip尾的独立完整gzip文件片段 - 直接将多个这类片段拼接写入同一个.gz文件时,标准gunzip工具支持顺序解压多个拼接的gzip流,所以只会提示尾部垃圾可忽略,但Python内置的
gzip模块默认只会读取第一个gzip流,后面的内容要么被判定为无效数据,要么直接抛出格式错误。
最优修复方案(改动最小、兼容性最好)
调整压缩逻辑,将压缩操作放到单例的写进程中执行,避免多段独立gzip流拼接:
- 工作进程仅处理文本,不做压缩,处理完成后直接将原始文本子块放到输出队列
- 写进程为每个输出文件初始化
gzip.GzipFile对象,直接写入原始文本,由gzip模块维护完整连续的压缩流,只会生成一套gzip头和尾,完全兼容所有gzip读取工具
修正后的核心代码示例
import gzip import multiprocessing as mp def process_text(inqueue, outqueue): while True: textblock = inqueue.get() if textblock: subblocks = dict() # 此处保留你原有的文本处理、拆分逻辑,直接存原始文本 outqueue.put(subblocks) else: outqueue.put(dict()) break def write_files(outqueue, outfiles, active_text_processors): while True: subblocks = outqueue.get() if subblocks: for id, subblock in subblocks.items(): outfiles[id].write(subblock) outfiles[id].flush() else: active_text_processors -= 1 if not active_text_processors: break # 初始化输出文件,直接用gzip.open创建压缩流对象 outfiles = { id: gzip.open(f'file_{id}.gz', 'wt', compresslevel=9) for id in ids } # 以下其他初始化逻辑保留原有即可,注意修正原有代码的几处笔误: # 1. 主进程攒够缓冲区后要put(lines)而不是put(buffersize) # 2. 工作进程终止信号是put(dict())不是调用write方法 # 3. writer进程的target要写write_files而不是writer # 4. 输出文件名的变量是id不是if
备选方案(需保留工作进程压缩逻辑时使用)
如果要利用多核做压缩降低写进程负载,就改用zlib模块做分块压缩,避免生成多段独立gzip头:
- 初始化zlib压缩对象时指定
wbits=31(对应gzip格式) - 每个子块压缩后仅写入压缩数据,不要单独加gzip尾
- 所有数据写完后统一调用
flush(zlib.Z_FINISH)写入最终的gzip尾即可
现有代码的其他明显bug
这些bug也可能导致输出文件损坏,需要同步修正:
process_text终止分支的outqueue.write(dict())改为outqueue.put(dict())- 主进程往输入队列放数据时的
inqueue.put(buffersize)改为inqueue.put(lines),同时重置lines = [] - 初始化writer进程时的
target = writer改为target = write_files - 输出文件名模板的
file_{if}.gz改为file_{id}.gz
内容的提问来源于stack exchange,提问作者mnmldani
相关产品推荐
相关产品推荐

