从Python设置gzip时间戳
我对使用Python的gzip
模块来压缩数据很感兴趣。其实,我希望压缩后的结果是可预测的,因为这样在很多情况下都很方便,比如说如果有其他程序要检查输出是否有变化,或者如果输出需要进行加密签名。
可惜的是,每次得到的输出都不一样。根据我的观察,造成这个问题的主要原因是gzip头部的时间戳字段,Python模块总是用当前时间来填充这个字段。我觉得gzip流里应该有时间戳,这样的规定真让人遗憾。
无论如何,Python的gzip
模块似乎没有提供一个方法,让我们可以指定底层数据的正确修改时间。(而实际的gzip
程序在可能的情况下会使用输入文件的时间戳。)我想这可能是因为,实际上只有在写入文件时,gunzip
命令才会关心时间戳——而现在,我也想要可预测的输出。难道这就这么难吗?
有没有人遇到过这个问题?
从Python中以任意时间戳压缩数据的gzip
,有什么比较好的方法吗?
7 个回答
2
提交一个补丁,把时间戳的计算部分单独提取出来。这个补丁几乎肯定会被接受。
8
是的,你没有什么好选择。时间是通过在 _write_gzip_header 这个函数里写的这一行来记录的:
write32u(self.fileobj, long(time.time()))
因为他们没有提供一个方法来覆盖这个时间,你可以做以下几件事:
- 从 GzipFile 这个类派生一个新类,然后把
_write_gzip_header
函数复制到你的新类里,只需在这一行中改个值。 - 在导入 gzip 模块后,给它的时间成员赋予新的代码。这样你实际上是在 gzip 代码中重新定义了时间这个名字,所以你可以改变 time.time() 的含义。
- 复制整个 gzip 模块,并把它命名为 my_stable_gzip,然后修改你需要的那一行。
- 传入一个 CStringIO 对象作为文件对象,在 gzip 完成后修改字节流。
- 写一个假的文件对象,记录写入的字节,并把所有内容传递给一个真实的文件,除了时间戳的字节,你自己来写。
这是选项 #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()