Java Zip/Jar文件中的二进制差异

Java Zip/Jar文件中的二进制差异,java,jar,zip,Java,Jar,Zip,似乎从完全相同的源文件构建jar或zip总是会生成不同的文件。我使用javajar命令以及ant中的jar和zip任务尝试了这一点 这似乎是因为新的jar/zip在每个存储的文件上都将时间戳设置为当前时间 有没有办法强迫zip工具只需在文件系统上的文件上使用时间戳,就可以确保从完全相同的源构建的jar看起来完全相同?我认为没有办法让zip做到这一点,但您肯定可以在文件系统上的文件上敲打时间戳到一个已知的日期(在unix下使用'touch'命令-我不知道在Windows下是什么)在创建jar之前。

似乎从完全相同的源文件构建jar或zip总是会生成不同的文件。我使用javajar命令以及ant中的jar和zip任务尝试了这一点

这似乎是因为新的jar/zip在每个存储的文件上都将时间戳设置为当前时间


有没有办法强迫zip工具只需在文件系统上的文件上使用时间戳,就可以确保从完全相同的源构建的jar看起来完全相同?

我认为没有办法让zip做到这一点,但您肯定可以在文件系统上的文件上敲打时间戳到一个已知的日期(在unix下使用'touch'命令-我不知道在Windows下是什么)在创建jar之前。

我遇到了一个类似的问题,正如pjz所建议的,我通过在将文件添加到jar之前对其进行'touch'(因此,它对我有效:-))。如果需要,您可以在GNU Windows实用程序中找到touch for Windows,core utils:,但这是一个很好的软件包(尽管您可能喜欢许多其他有用的实用程序),或者下载类似的软件。

好的,我和一位同事想出了一个适合我们的解决方案

我们没有重新设计整个构建过程以不删除任何类或jar文件,而是使用以下过程:

  • 构建新的工件
  • 使用jardiff(的一部分)来比较以前版本的更改
  • 如果jardiff生成的diff jar没有更改,则从以前的构建中获取工件

  • 是的,我知道这听起来很难理解,但考虑到这一点肯定比重写构建脚本要好。此外,我们可以在新机器上进行完全干净的构建(在服务器出现故障的情况下),这个过程将确保只生成实际更新的JAR。

    二进制差异是因为清单文件的时间戳。 如果让jar自己创建清单,它将在 飞行并将创建的清单设置为currentTimeMillis。

    您可以通过:
    1.不要添加清单(如果您使用ant,则必须使用zip而不是jar)

    2.像添加普通文件一样添加清单。(因此清单是文件系统中的一个文件,而不是动态创建的)

    使用Java.util.zip.ZipOutputStream标准库实用程序,可以创建具有可复制内容的zip文件

    唯一的技巧是必须使用以下技巧固定zip条目的时间戳:

    ZipOutputStream zos=...;
    ZipEntry ze=new ZipEntry("Filename");
    zipEntry.setTime(0);
    zos.putNextEntry(ze);
    try
    {
       zos.write(data);
    }finally
    {
      zos.closeEntry();
    }
    

    这个答案不够充分。读我的另一个答案。我没有删除这个答案,因为它显示了非二进制兼容性的一些原因,但不是所有原因

    我有一个精心设计的答案,但不幸的是用德语: 简短介绍:

    echo compile javac
    $JAVAC_HOME/bin/javac -d $TMPJAVAC/binjar -cp $CLASSPATH -sourcepath $SRCPATH $FILE1SRC 
    mkdir $TMPJAVAC/binjar/META-INF
    ##Note: create the manifest file manually, not with jar, because of time stamp
    cp $MANIFEST $TMPJAVAC/binjar/META-INF/MANIFEST.MF  
    echo touch timestams to $VERSION
    find $TMPJAVAC/binjar -exec touch -d $VERSION {} \;
    echo build jar
    $JAVAC_HOME/bin/jar -cvfM $JARFILE -C $TMPJAVAC/binjar . > $TMPJAVAC/jar.txt
    if ! test "$MD5FILE" = ""; then echo output MD5 checksum
      md5sum -b $JARFILE > $MD5FILE
    fi  
    echo ok $JARFILE
    
    它是一个由脚本变量集从外部控制的通用shell脚本。 可以从另一个shell脚本或gradle调用它。 重要的是清单的touch命令和copy命令,jar的M选项(不是M)。jar文件中的二进制差异来自所包含文件的时间戳。
    Hartmut Schorrig

    不幸的是,我在2020-03-17的回答并没有在所有情况下生成可复制的jar文件(二进制兼容)。原因:jar中文件的顺序取决于随机性。在另一个maschine上生成,我使用了Windows和Linux,在jar/zip文件中生成另一个文件顺序,从而生成另一个二进制内容。如果对jar文件逐个文件进行比较(解压后),则它们是相同的。但纯二进制jar并非如此。 我有一个解决方案,它不使用来自JDK的jar命令,而是使用JRE功能的自己的jar算法。JRE包含java.util.jar.*和java.util.zip.*作为标准。人们可以从中读到描述。您可以下载一个具有给定MD5校验和的小型文件(请访问此下载/版本存档页)。本文包含示例。
    我已经用Windows和Linux测试了不同的JDK版本,结果是二进制兼容的。

    fwiw,任何使用gradle构建系统的人都可以便宜/容易地获得二进制稳定的jar:

    tasks.withType(Jar).configureEach {
        Jar jar ->
            jar.preserveFileTimestamps = false
            jar.reproducibleFileOrder = true
    }
    

    这就是其他答案所建议的:确保时间戳是稳定的,并且确保jar中条目的顺序也是稳定的。我相信这里的大脑在
    jar
    命令本身之外,但我没有在这个问题上投入足够的资金来检查。

    Windows上有触摸实用程序。但据我理解,这并不能解决问题。问题似乎是zip中的条目获取的是当前时间,而不是文件上的时间。Touch不会解决这个问题。不,真正的问题是,如果你重新生成一个文件——即使你生成的文件内容相同——它会得到上次重新生成的mtime时间戳,因此如果你压缩该文件,因为zip存储mtime,即使内容相同,它也会不同。Cheeso是正确的。即使使用完全相同的源文件并构建两次jar,也会导致jar中的时间戳发生变化。但是,您不能让
    jar
    生成清单。我先尝试了这个,遗憾的是,它不起作用。请参阅上面Cheeso的评论。如果您正在寻找构建确定性JAR(可重复的SHA1签名)的分步说明,这里有一篇文章:JAR的格式与ZIP略有不同。JAR是基于ZIP的,但它包含清单文件和签名,而ZIP没有。不过,我想知道这是否会创建一个二进制文件。不过,加里·罗(Gary Rowe)的回答(在评论中)可能比我的jardiff hack要好。