我正在使用 aZipOutputStream来压缩一堆文件,这些文件是已压缩格式以及许多大型高度可压缩格式(如纯文本)的混合。
大多数已压缩的格式都是大文件,花费 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();
Run Code Online (Sandbox Code Playgroud)
小智 3
CRC鉴于我必须解决这个问题,我无法确定仅读取一次文件并使用标准库计算的方法。
我确实找到了一种优化方法,可以平均减少时间50%。
我预先计算要CRC同时存储的文件的数量并ExecutorCompletionService限制Runtime.getRuntime().availableProcessors()并等待它们完成。其有效性根据需要计算的文件数量而变化CRC。文件越多,好处就越大。
然后,.postVisitDirectories()我将临时运行的一对中的ZipOutputStreama包裹起来,将其转换为我可以传递到的 a ,以将结果上传到远程服务器,同时串行写入所有预先计算的对象。PipedOutputStreamPipedInputStream/PipedOutputStreamThreadZipOutputStreamInputStreamHttpRequestZipOutputStreamZipEntry/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,开销比读取两次文件要高。
| 归档时间: |
|
| 查看次数: |
1823 次 |
| 最近记录: |