从Python设置gzip时间戳

10 投票
7 回答
3558 浏览
提问于 2025-04-11 19:23

我对使用Python的gzip模块来压缩数据很感兴趣。其实,我希望压缩后的结果是可预测的,因为这样在很多情况下都很方便,比如说如果有其他程序要检查输出是否有变化,或者如果输出需要进行加密签名。

可惜的是,每次得到的输出都不一样。根据我的观察,造成这个问题的主要原因是gzip头部的时间戳字段,Python模块总是用当前时间来填充这个字段。我觉得gzip流里应该有时间戳,这样的规定真让人遗憾。

无论如何,Python的gzip模块似乎没有提供一个方法,让我们可以指定底层数据的正确修改时间。(而实际的gzip程序在可能的情况下会使用输入文件的时间戳。)我想这可能是因为,实际上只有在写入文件时,gunzip命令才会关心时间戳——而现在,我也想要可预测的输出。难道这就这么难吗?

有没有人遇到过这个问题?

从Python中以任意时间戳压缩数据的gzip,有什么比较好的方法吗?

7 个回答

2

提交一个补丁,把时间戳的计算部分单独提取出来。这个补丁几乎肯定会被接受。

8

是的,你没有什么好选择。时间是通过在 _write_gzip_header 这个函数里写的这一行来记录的:

write32u(self.fileobj, long(time.time()))

因为他们没有提供一个方法来覆盖这个时间,你可以做以下几件事:

  1. 从 GzipFile 这个类派生一个新类,然后把 _write_gzip_header 函数复制到你的新类里,只需在这一行中改个值。
  2. 在导入 gzip 模块后,给它的时间成员赋予新的代码。这样你实际上是在 gzip 代码中重新定义了时间这个名字,所以你可以改变 time.time() 的含义。
  3. 复制整个 gzip 模块,并把它命名为 my_stable_gzip,然后修改你需要的那一行。
  4. 传入一个 CStringIO 对象作为文件对象,在 gzip 完成后修改字节流。
  5. 写一个假的文件对象,记录写入的字节,并把所有内容传递给一个真实的文件,除了时间戳的字节,你自己来写。

这是选项 #2 的一个例子(未经测试):

class FakeTime:
    def time(self):
        return 1225856967.109

import gzip
gzip.time = FakeTime()

# Now call gzip, it will think time doesn't change!

选项 #5 可能是最干净的,因为它不依赖于 gzip 模块的内部实现(未经测试):

class GzipTimeFixingFile:
    def __init__(self, realfile):
        self.realfile = realfile
        self.pos = 0

    def write(self, bytes):
        if self.pos == 4 and len(bytes) == 4:
            self.realfile.write("XYZY")  # Fake time goes here.
        else:
            self.realfile.write(bytes)
        self.pos += len(bytes)
7

从Python 2.7开始,你可以指定在gzip头部使用的时间。需要注意的是,文件名也包含在头部中,并且也可以手动指定。

import gzip

content = b"Some content"
f = open("/tmp/f.gz", "wb")
gz = gzip.GzipFile(fileobj=f,mode="wb",filename="",mtime=0)
gz.write(content)
gz.close()
f.close()

撰写回答