我正在使用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();
答案 0 :(得分:1)
我无法确定一种只读取文件一次的方法,并根据我必须解决此问题的时间用标准库计算CRC
。
我确实发现了一项优化,平均时间减少了大约50%
。
我预先计算要与CRC
同时存储的文件的ExecutorCompletionService
,并等到Runtime.getRuntime().availableProcessors()
,并等待它们完成。其有效性取决于需要CRC
计算的文件数。文件越多,效益越大。
然后在.postVisitDirectories()
中,我在ZipOutputStream
对PipedOutputStream
对PipedInputStream/PipedOutputStream
对,在Thread
上运行,以转换ZipOutputStream
} InputStream
我可以传入HttpRequest
将ZipOutputStream
的结果上传到远程服务器,同时连续编写所有预先计算的ZipEntry/Path
个对象。
现在这已经足够好了,可以处理300+GB
的即时需求,但是当我进入10TB
工作时,我会考虑解决这个问题并试图找到更多优势而不会增加很复杂。
如果我想出一些明智的时间,我会用新的实现更新这个答案。
我最后写了一个干净的房间ZipOutputStream
,支持多部分zip文件,智能压缩级别与STORE
,并且能够在我读取时计算CRC
,然后写出元数据在流的最后。
ZipOutputStream.setLevel(NO_COMPRESSION/DEFAULT_COMPRESSION)
黑客不是一个可行的方法。我对数百个进行了大量的测试 数据演出,成千上万的文件夹和文件以及测量结果 确凿。它在计算CRC
时没有任何好处STORED
个文件与NO_COMPRESSION
压缩文件相比。实际上 慢大幅度上升!在我的测试中,文件位于网络安装驱动器上,因此请阅读 文件已经通过网络压缩文件两次到 计算
CRC
然后再次添加到ZipOutputStream
为 比仅DEFLATED
处理所有文件一样快或快 并更改.setLevel()
上的ZipOutputStream
。网络访问没有进行本地文件系统缓存。 这是一种更糟糕的情况,处理本地磁盘上的文件会 由于本地文件系统缓存,速度要快得多。
所以这种黑客行为是一种天真的做法,并且基于错误的假设。正在处理 数据通过压缩算法,即使在
NO_COMPRESSION
级别 并且开销高于两次读取文件。