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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/753576/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 13:38:34  来源:igfitidea点击:

Binary Difference in Zip/Jar file

javajarzip

提问by Mike Miller

It seems like building a jar or zip from the exact same source files will always yield a different file. I tried this both using the java jar command, and the jar and zip tasks in ant.

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

It appears to be due to the fact that new jars/zips have the timestamp set to the current time on each stored file.

这似乎是由于新 jars/zip 的时间戳设置为每个存储文件的当前时间。

Is there a way to force a zip tool to simply use the timestamp on the file on the filesystem to ensure that a jar built from the exact same source will appear exactly the same?

有没有办法强制 zip 工具简单地使用文件系统上文件的时间戳来确保从完全相同的源构建的 jar 看起来完全相同?

采纳答案by Mike Miller

Ok, a coworker and I came up with a solution that works for us.

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

Instead of reengineering our entire build process to not delete any class or jar files, we use this procedure:

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

  1. Build new artifacts.
  2. Use jardiff (part of jnlp) to compare changes from previous build.
  3. If the diff jar that jardiff produces has no changes, get artifact from previous build.
  1. 构建新的工件。
  2. 使用 jardiff (jnlp 的一部分)来比较先前构建的更改。
  3. 如果 jardiff 生成的 diff jar 没有更改,则从以前的构建中获取工件。

Yeah, I know it sounds kludgy, but it sure beats rewriting build script to take this into account. Also, we can do a completely clean build on a fresh machine (in the case of server failure), and this process will ensure that only actually updated jars are produced.

是的,我知道这听起来很笨拙,但考虑到这一点,它确实胜过重写构建脚本。此外,我们可以在一台新机器上进行完全干净的构建(在服务器故障的情况下),这个过程将确保只生成实际更新的 jar。

回答by Kees

The binary difference is because of the timestamp of the manifest files. If you let jar create a manifest itself it will create a manifest on the fly and set the created manifest to currentTimeMillis.

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

You can solve it by:
1. Do not add a manifest (if your using ant you must use zip instead of jar)
2. Add the manifest like you add normal files. (So the manifest is a file on your filesystem and it isn't created on the fly)

您可以通过以下方式解决:
1. 不要添加清单(如果您使用 ant,则必须使用 zip 而不是 jar)
2. 像添加普通文件一样添加清单。(所以清单是文件系统上的一个文件,它不是动态创建的)

回答by András

Using the Java java.util.zip.ZipOutputStream standard library utility it is possible to create zip files with reproducible content.

使用 Java java.util.zip.ZipOutputStream 标准库实用程序可以创建具有可重现内容的 zip 文件。

The only trick is that the timestamp of the zip entries must be fixed using this trick:

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

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

回答by pjz

I don't think there's a way to make zip do that, but you could certainly hammer the timestamps on the files on the filesystem to a known date (using the 'touch' command under unix - I dunno what under Windows) before you create the jar.

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

回答by Gadi

I had a similar problem and, as pjz suggests, I solved it by 'touch'-ing the files before adding them to the jar (so, it worked for me :-) ). You can find touch for Windows, if you need it, either in the GNU Windows Utilities, core utils: http://gnuwin32.sourceforge.net/packages/coreutils.htm, but it's a big package for just this single one (though there are many other useful utilities there you may like), or alternatively, download something like http://www.softpedia.com/progClean/Touch-for-Windows-Clean-41086.html.

我遇到了类似的问题,正如 pjz 所建议的那样,我通过在将文件添加到 jar 之前“触摸”文件来解决它(因此,它对我有用:-))。如果需要,您可以在 GNU Windows 实用程序、核心实用程序中找到适用于 Windows 的触摸:http: //gnuwin32.sourceforge.net/packages/coreutils.htm,但它是一个大包(尽管还有许多其他有用的实用程序,您可能会喜欢),或者下载类似http://www.softpedia.com/progClean/Touch-for-Windows-Clean-41086.html 的内容

回答by Hartmut Schorrig

This answer is insufficent. Read my other answer. I have not removed this answer because it shows some reason for non binary-compatibility, but not all reasons.

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

I have an elaborately answer, but unfortunately in german: https://www.vishia.org/SwEng/pdf/GenerateRepeatability_de.pdfShort presentation:

我有一个精心的答案,但不幸的是用德语:https: //www.vishia.org/SwEng/pdf/GenerateRepeatability_de.pdf简短介绍:

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

It is a universal shell script controlled by script variable set from outside. It can be called from another shell script or from gradle. The essential is the touch command and the copy command for the manifest, and the M option (not m) for jar. Binary differences in jar file came from the time stamps of the contained files. Hartmut Schorrig

它是由外部设置的脚本变量控制的通用 shell 脚本。它可以从另一个 shell 脚本或 gradle 调用。必不可少的是清单的 touch 命令和复制命令,以及 jar 的 M 选项(不是 m)。jar 文件中的二进制差异来自所包含文件的时间戳。哈特穆特·肖里格

回答by Hartmut Schorrig

Unfortuantelly my answer from 2020-03-17 does not produce a reproducible jar file (binary compatible) in all situations. The reason: The order of files in the jar depends on randomness. Generation on another maschine, I have used Windows and Linux, produces another file order in the jar/zip file which results in another binary content. If the jar files are compared file by file (after unzip), they are identically. But the pure binary jar is not so. I have a solution which does not use the jar command from the JDK but an own jar algorithm using the capability of JRE. The JRE contains java.util.jar.* and java.util.zip.* as standard. One can read the description in https://vishia.org/Java/html5/source+build/reproducibleJar.html. You can download a small https://www.vishia.org/Java/Download/versionArchive/vishiaZipJar-2020-03-23.jarwith given MD5 check sum (visit this Download/versionArchive page). The article contains examples. I have tested with Windows and Linux, which different JDK versions, the results are binary compatible.

不幸的是,我在 2020-03-17 的回答在所有情况下都不会产生可重现的 jar 文件(二进制兼容)。原因:jar 中文件的顺序取决于随机性。在另一台机器上生成,我使用过 Windows 和 Linux,在 jar/zip 文件中生成另一个文件顺序,从而产生另一个二进制内容。如果将 jar 文件逐个文件(解压缩后)进行比较,它们是相同的。但纯二进制 jar 并非如此。我有一个解决方案,它不使用来自 JDK 的 jar 命令,而是使用 JRE 功能的自己的 jar 算法。JRE 包含 java.util.jar.* 和 java.util.zip.* 作为标准。可以阅读https://vishia.org/Java/html5/source+build/reproducibleJar.html 中的描述。你可以下载一个小https://www.vishia.org/Java/Download/versionArchive/vishiaZipJar-2020-03-23.jar带有给定的 MD5 校验和(访问此下载/versionArchive 页面)。文章包含示例。我用Windows和Linux测试过,不同的JDK版本,结果是二进制兼容的。