0

以下代码为两个字符串生成确定性(shasum 相同)的文件。

    try(
            FileOutputStream fos = new FileOutputStream(saveLocation);
            GZIPOutputStream zip = new GZIPOutputStream(fos, GZIP_BUFFER_SIZE);
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zip, StandardCharsets.UTF_8));
            ){
        writer.append(str);
    }

生产:

a.gz f0200d53f7f9b35647b5dece0146d72cd1c17949

但是,如果我在命令行上获取文件并重新压缩它,它会产生不同的结果

> gunzip -n a.gz ;gzip -n a ; shasum a.gz 

50f478a9ceb292a2d14f1460d7c584b7a856e4d9  a.gz

如何使用 /usr/bin/gzip 和 gunzip 使其与原始 sha 匹配?

4

1 回答 1

1

我认为问题很可能是 Gzip 文件头。

  • Gzip 格式规定在文件头中包含文件名和文件时间戳。(我看到你-n在解压缩和重新压缩时使用...这在这里可能是正确的。)

  • Gzip 格式还在标题中包含“操作系统 ID”。这应该识别源文件系统类型;例如 0 代表 FAT,3 代表 UNIX,等等。

这些中的任何一个都可能导致 Gzip 文件的差异,从而导致不同的哈希值。

如果我要自己解决这个问题,我会首先使用cmp查看压缩文件差异的开始位置,然后od确定差异是什么。请参阅 Gzip 文件格式规范以了解差异的含义:

  • RFC 1952 - GZIP 文件格式规范版本 4.3
  • 维基百科的gzip页面。

如何使用gzipand使其与原始 SHA 匹配gunzip

假设差异是操作系统 id,我认为没有一种实用的方法可以使用gzipandgunzip命令解决这个问题。


我查看了GZIPOutputStreamJava 11 的源代码,但并不乐观。

  • 它将时间戳硬连接为零。
  • 它将操作系统标识符硬连接为零(这应该意味着 FAT)。

硬连线在一个private方法中,几乎不可能通过子类化或反射来“修复”。您可以复制代码并以这种方式修复它,但是您必须GZIPOutputStream无限期地维护您的变体类。

(我会考虑更改应用程序......或其他任何东西......这样我就不需要校验和相同。你没有说你为什么要这样做。它仅用于测试目的,尝试寻找以不同的方式来实现测试。)

于 2020-02-16T01:52:23.463 回答