如何压缩大量相似的文件?



在java中,我有一个由30_000项组成的List<byte[]>(每个约1.2kB大小)。我想把数据保存到磁盘上。这些字节数组都很相似(每个都表示utf-8编码的json-Data),但不完全相同。

目前,我使用ZipOutputStream将每个字节数组作为一个文件写入一个大的zip-File。

import java.util.zip.*;
var fileCount = 0;
try (var out = new ZipOutputStream(new FileOutputStream("out.zip"))) {
for (var byteArray : listOfbyteArrays) {
out.putNextEntry(new ZipEntry((fileCount++)+".json")));
out.write(byteArray);
}
}
然而,这个解决方案创建了一个压缩效果很差的zip文件。每个文件的压缩系数约为50%。但是不使用文件的相似性:
out.zip compression 45%
1.json compression 45%
2.json compression 46%
3.json compression 44%
4.json compression 45%
...

如果我在windows资源管理器中压缩结果(在zip中创建zip),压缩后的压缩率要高得多(大约是99%):

zipInZip.zip compression 99%
out.zip compression 45%
1.json compression 45%
2.json compression 46%
3.json compression 44%
4.json compression 45%
...

如何在java中创建一个zip文件,使用文件的相似性来归档高压缩?

ZIP文件并不真正适合于此。有两个原因:

  • 短文件不能很好地压缩,ZIP单独压缩归档中的每个文件。
  • ZIP的DEFLATE压缩算法相当过时。

如果存档中的文件一起压缩而不是单独压缩,您将获得更好的压缩效果。

因此,创建未压缩JSON文件的TAR存档,然后使用GZIP压缩压缩整个文件。你需要使用第三方的TAR I/O库。


另一个选择是使用标准的ZipOutputStream来创建一个没有压缩的ZIP文件(将压缩级别设置为0),然后GZIP它。

压缩一个没有压缩的ZIP文件可以工作…但是(在我看来)它很笨拙。

标准的ZipOutputStream只支持DEFLATE,但是有第三方的Java ZIP文件I/O实现支持其他压缩算法。但是,仅仅让ZIP使用更新的算法并不能解决小文件效应。

压缩许多类似的文件是zip文件的已知限制。使用"zip-in-a-zip"方法进行高压缩。尽管对于消费者来说,要解压缩两次可能会很笨重。

try (var compressed = new ZipOutputStream(new FileOutputStream("zipInZip.zip"))) {
compressed.putNextEntry(new ZipEntry("out.zip"));
try (var uncompressedContainer = new ZipOutputStream(compressed)) {
uncompressedContainer.setLevel(Deflater.NO_COMPRESSION);
for (var byteArray: listOfbyteArrays) {
uncompressedContainer.putNextEntry(new ZipEntry((fileCount++)+".json")));
uncompressedContainer.write(byteArray);
}
}
}

最新更新