我正在使用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();
2条答案
按热度按时间hfyxw5xn1#
简短回答:
考虑到我必须解决这个问题的时间,我无法确定一种方法来只读取文件一次并使用标准库计算
CRC
。我确实发现了一个优化,平均减少了大约
50%
的时间。我预先计算要与
ExecutorCompletionService
(限制为Runtime.getRuntime().availableProcessors()
)并发存储的文件的CRC
,并等待它们完成。其有效性根据需要计算CRC
的文件数量而有所不同。文件越多,收益越大。然后在
.postVisitDirectories()
中,我将ZipOutputStream
Package 在运行在临时Thread
上的PipedInputStream/PipedOutputStream
对的PipedOutputStream
周围,以将ZipOutputStream
转换为InputStream
,我可以将InputStream
传递到HttpRequest
中,以将ZipOutputStream
的结果上传到远程服务器,同时串行写入所有预先计算的ZipEntry/Path
对象。现在,这已经足够好了,可以处理
300+GB
的即时需求,但是当我开始处理10TB
的工作时,我将考虑解决它,并试图在不增加太多复杂性的情况下找到更多的优点。如果我想出了一些实质上更好的时间明智的,我将更新这个答案与新的实现。
长回答:
我最终编写了一个净室
ZipOutputStream
,它支持多部分zip文件,智能压缩级别vsSTORE
,并且能够在读取时计算CRC
,然后在流的末尾写出元数据。为什么ZipOutputStream.setLevel()交换不起作用:
ZipOutputStream.setLevel(NO_COMPRESSION/DEFAULT_COMPRESSION)
黑客不是一个可行的方法。我对数百个数据、数千个文件夹和文件进行了广泛的测试,测量结果是决定性的。它在计算STORED
文件的CRC
与将它们压缩到NO_COMPRESSION
之间没有任何好处。居然慢了一大截!在我的测试中,文件位于网络安装的驱动器上,因此通过网络阅读已经压缩的文件两次以计算
CRC
,然后再次添加到ZipOutputStream
,与只处理一次所有文件DEFLATED
并在ZipOutputStream
上更改.setLevel()
一样快或更快。网络访问没有本地文件系统缓存。这是一个更糟糕的情况,因为本地文件系统缓存,在本地磁盘上处理文件的速度要快得多。
因此,这种黑客攻击是一种天真的方法,是基于错误的假设。即使在
NO_COMPRESSION
级别,它也通过压缩算法处理数据,开销高于阅读文件两次。ecr0jaav2#
考虑到我必须解决这个问题的时间,我无法确定一种方法来只读取文件一次并使用标准库计算
CRC
。我确实发现了一个优化,平均减少了大约
50%
的时间。我预先计算要并发存储的文件的
CRC
...与交替使用
ZipOutputStream.setLevel(Deflater.NO_COMPRESSION)
和ZipOutputStream.setLevel(Deflater.DEFAULT_COMPRESSION)
相比,我已经测量了相同的改进,而没有并发的CRC计算:(The
relativize(Path) : String
方法不在示例中。)CRC32
类提供了非常有效的update(ByteBuffer)
方法,用于内存Map(直接)文件缓冲区。