ZipEntry.STORED用于已经压缩的文件?

时间:2016-02-01 22:24:54

标签: java zipoutputstream

我正在使用ZipOutputStream压缩一堆文件,这些文件是已压缩格式的混合文件以及许多大型高度可压缩格式(如纯文本)。

大多数已经压缩的格式都是大文件,将cpu和内存用于重新压缩它们是没有意义的,因为它们永远不会变小,有时会在极少数情况下变得稍大。

我在检测到预压缩文件时尝试使用.setMethod(ZipEntry.STORED),但它抱怨我需要为这些文件提供size, compressedSize and crc

我可以使用以下方法使用它,但这需要我读取文件两次。一旦计算CRC32,然后再次将文件实际复制到ZipOutputStream

// code that determines the value of method omitted for brevity
if (STORED == method)
{
    fze.setMethod(STORED);
    fze.setCompressedSize(fe.attributes.size());
    final HashingInputStream his = new HashingInputStream(Hashing.crc32(), fis);
    ByteStreams.copy(his,ByteStreams.nullOutputStream());
    fze.setCrc(his.hash().padToLong());
}
else
{
    fze.setMethod(DEFLATED);
}
zos.putNextEntry(fze);
ByteStreams.copy(new FileInputStream(fe.path.toFile()), zos);
zos.closeEntry();

是否有办法提供此信息而无需两次读取输入流?

1 个答案:

答案 0 :(得分:1)

简答:

我无法确定一种只读取文件一次的方法,并根据我必须解决此问题的时间用标准库计算CRC

我确实发现了一项优化,平均时间减少了大约50%

我预先计算要与CRC同时存储的文件的ExecutorCompletionService,并等到Runtime.getRuntime().availableProcessors(),并等待它们完成。其有效性取决于需要CRC计算的文件数。文件越多,效益越大。

然后在.postVisitDirectories()中,我在ZipOutputStreamPipedOutputStreamPipedInputStream/PipedOutputStream对,在Thread上运行,以转换ZipOutputStream } InputStream我可以传入HttpRequestZipOutputStream的结果上传到远程服务器,同时连续编写所有预先计算的ZipEntry/Path个对象。

现在这已经足够好了,可以处理300+GB的即时需求,但是当我进入10TB工作时,我会考虑解决这个问题并试图找到更多优势而不会增加很复杂。

如果我想出一些明智的时间,我会用新的实现更新这个答案。

答案很长:

我最后写了一个干净的房间ZipOutputStream,支持多部分zip文件,智能压缩级别与STORE,并且能够在我读取时计算CRC,然后写出元数据在流的最后。

为什么ZipOutputStream.setLevel()交换不起作用:

  

ZipOutputStream.setLevel(NO_COMPRESSION/DEFAULT_COMPRESSION)   黑客不是一个可行的方法。我对数百个进行了大量的测试   数据演出,成千上万的文件夹和文件以及测量结果   确凿。它在计算CRC时没有任何好处   STORED个文件与NO_COMPRESSION压缩文件相比。实际上   大幅度上升!

     

在我的测试中,文件位于网络安装驱动器上,因此请阅读   文件已经通过网络压缩文件两次到   计算CRC然后再次添加到ZipOutputStream为   比仅DEFLATED处理所有文件一样快或快   并更改.setLevel()上的ZipOutputStream

     

网络访问没有进行本地文件系统缓存。   这是一种更糟糕的情况,处理本地磁盘上的文件会   由于本地文件系统缓存,速度要快得多。

     

所以这种黑客行为是一种天真的做法,并且基于错误的假设。正在处理   数据通过压缩算法,即使在NO_COMPRESSION级别   并且开销高于两次读取文件。