Python gzip 可重复压缩:内容相同,时间戳和文件名为何仍会改变输出

10-01 4阅读

解压后一样,压缩包却总在变化

构建脚本每次生成相同文本,再压缩成 gzip,发布系统却认为产物发生了变化。检查解压内容完全相同,直接比较压缩字节又确实不同。gzip 除了保存压缩数据,还可以在头部记录时间和原始文件名,差异不一定来自业务内容。

如果这些产物用于内容缓存、重复构建核对或去重,需要先定义比较目标。只关心解压后的资料,可以比较原始内容;如果要求压缩产物本身稳定,就要把输入字节、压缩参数、元数据和工具环境一起纳入约定。

一次只改变一个变量

下面代码使用 BytesIO,不创建真实文件,已在 Python 3.12 验证。两个固定时间代替实际等待,使每次运行都能复现差异。pack 明确指定压缩级别、二进制写入模式和文件名,避免示例受到默认参数或文件对象名称影响。

Python gzip 可重复压缩:内容相同,时间戳和文件名为何仍会改变输出

AI概念示意图,非真实界面

import gzip
from io import BytesIO

payload = b'alpha=1\nbeta=2\n'

def pack(name, timestamp):
    buffer = BytesIO()
    with gzip.GzipFile(
        filename=name, mode='wb', fileobj=buffer,
        compresslevel=6, mtime=timestamp,
    ) as stream:
        stream.write(payload)
    return buffer.getvalue()

first = pack('report.txt', 100)
later = pack('report.txt', 101)
renamed = pack('other.txt', 100)
stable_a = pack('', 0)
stable_b = pack('', 0)
all_blobs = (first, later, renamed, stable_a, stable_b)

print('timestamp changes bytes:', first != later)
print('filename changes bytes:', first != renamed)
print('same settings repeat:', stable_a == stable_b)
print('all payloads match:', all(
    gzip.decompress(blob) == payload for blob in all_blobs
))
assert first != later and first != renamed
assert stable_a == stable_b
assert all(gzip.decompress(blob) == payload for blob in all_blobs)

四个真值分别证明什么

预期四行分别是 timestamp changes bytes: True、filename changes bytes: True、same settings repeat: True 和 all payloads match: True。前两行固定内容,分别只变时间和名称;第三行固定全部显式设置;最后一行确认所有产物仍能还原相同输入。

GzipFile 的 mtime 省略或设为 None 时会使用当前时间。显式设为零可避免本次运行时刻进入头部。这里的 filename 是压缩流里的原始名称信息:即使传入的是同一个内存缓冲对象,不同名称仍可能产生不同压缩字节。

传入空字符串可以让示例不写原始文件名。若业务必须保留名称,也可以选择稳定名称,但应保证它不是临时路径或构建目录的随机部分。不要把外部保存 gzip 文件的路径,与流内部是否带有原始名称混为一谈。

先结束压缩流,再取完整字节

getvalue 放在 GzipFile 的 with 块结束之后。关闭压缩对象会完成流的收尾工作,而不会关闭传入的 BytesIO,所以这时仍可取出完整结果。若提前读取内存缓冲区,拿到的可能是不完整产物,造成与元数据无关的差异。

本文只验证同一实现、同一运行环境和相同参数下重复执行得到一致字节,不保证不同 Python、zlib 版本或不同压缩 API 的产物逐字节相同。合法的压缩编码可能不止一种,工具升级也可能改变实现和默认值。

需要长期可重复构建时,应固定解释器与压缩库环境,记录压缩级别,并保存一份小型参考输入作回归检查。升级后分别验证能否解压、内容是否一致和压缩字节是否一致,这三项结果帮助判断变化属于兼容性还是产物表示。

原始输入也必须稳定。相同的可见文本,可能因为编码、换行或字段排列不同而变成不同字节。示例直接使用固定 bytes,避免把这些变量夹在压缩实验里。实际管线应先把内容规范化,再决定哪些元数据必须被保留。

最后,不要把相同压缩字节当成来源可信的证明。这一流程解决的是构建和比较的一致性;产物来自谁、是否经过授权发布,仍由你已有的可信发布流程确认。两类检查可以相互补充,但不能互相代替。

参考资料

  1. Python 官方文档:gzip.GzipFile 与时间戳

  2. RFC 1952:gzip 文件格式与头部字段

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。