ZipEntry.STORED 对于已经压缩的文件?
ZipEntry.STORED for files that are already compressed?
我正在使用 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();
有没有一种方法可以提供此信息而无需两次读取输入流?
简答:
考虑到我必须解决这个问题的时间,我无法确定只读取一次文件并使用标准库计算 CRC
的方法。
我确实找到了一个优化,平均减少了大约 50%
的时间。
我预先计算了 CRC
个要同时存储的文件,ExecutorCompletionService
个限制为 Runtime.getRuntime().availableProcessors()
个,然后等到它们完成。此方法的有效性因需要 CRC
计算的文件数量而异。文件越多,收益越大。
然后在 .postVisitDirectories()
中,我将 ZipOutputStream
包裹在临时 Thread
上的 PipedInputStream/PipedOutputStream
对 运行 中的 PipedOutputStream
将 ZipOutputStream
转换为 InputStream
我可以传入 HttpRequest
以将 ZipOutputStream
的结果上传到远程服务器,同时写入所有预先计算的 ZipEntry/Path
对象连续。
目前这已经足够了,可以处理 300+GB
的即时需求,但是当我开始 10TB
工作时,我会着眼于解决它并尝试找到更多优势而不需要增加太多的复杂性。
如果我在时间上想出了更好的方法,我会用新的实现更新这个答案。
长答案:
我最终写了一个干净的房间 ZipOutputStream
支持多部分 zip 文件,智能压缩级别与 STORE
并且能够计算 CRC
我读然后写出来流末尾的元数据。
为什么 ZipOutputStream.setLevel() 交换不起作用:
The ZipOutputStream.setLevel(NO_COMPRESSION/DEFAULT_COMPRESSION)
hack is not a viable approach. I did extensive tests on hundreds of
gigs of data, thousands of folders and files and the measurements were
conclusive. It gains nothing over calculating the CRC
for the
STORED
files vs compressing them at NO_COMPRESSION
. It is actually
slower by a large margin!
In my tests the files are on a network mounted drive so reading
the files already compressed files twice over the network to
calculate the CRC
then again to add to the ZipOutputStream
was as
fast or faster than just processing all the files once as DEFLATED
and changing the .setLevel()
on the ZipOutputStream
.
There is no local filesystem caching going on with the network access.
This is a worse case scenario, processing files on the local disk will
be much much faster because of local filesystem caching.
So this hack is a naive approach and is based on false assumptions. It is processing the
data through the compression algorithm even at NO_COMPRESSION
level
and the overhead is higher than reading the files twice.
我正在使用 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();
有没有一种方法可以提供此信息而无需两次读取输入流?
简答:
考虑到我必须解决这个问题的时间,我无法确定只读取一次文件并使用标准库计算 CRC
的方法。
我确实找到了一个优化,平均减少了大约 50%
的时间。
我预先计算了 CRC
个要同时存储的文件,ExecutorCompletionService
个限制为 Runtime.getRuntime().availableProcessors()
个,然后等到它们完成。此方法的有效性因需要 CRC
计算的文件数量而异。文件越多,收益越大。
然后在 .postVisitDirectories()
中,我将 ZipOutputStream
包裹在临时 Thread
上的 PipedInputStream/PipedOutputStream
对 运行 中的 PipedOutputStream
将 ZipOutputStream
转换为 InputStream
我可以传入 HttpRequest
以将 ZipOutputStream
的结果上传到远程服务器,同时写入所有预先计算的 ZipEntry/Path
对象连续。
目前这已经足够了,可以处理 300+GB
的即时需求,但是当我开始 10TB
工作时,我会着眼于解决它并尝试找到更多优势而不需要增加太多的复杂性。
如果我在时间上想出了更好的方法,我会用新的实现更新这个答案。
长答案:
我最终写了一个干净的房间 ZipOutputStream
支持多部分 zip 文件,智能压缩级别与 STORE
并且能够计算 CRC
我读然后写出来流末尾的元数据。
为什么 ZipOutputStream.setLevel() 交换不起作用:
The
ZipOutputStream.setLevel(NO_COMPRESSION/DEFAULT_COMPRESSION)
hack is not a viable approach. I did extensive tests on hundreds of gigs of data, thousands of folders and files and the measurements were conclusive. It gains nothing over calculating theCRC
for theSTORED
files vs compressing them atNO_COMPRESSION
. It is actually slower by a large margin!In my tests the files are on a network mounted drive so reading the files already compressed files twice over the network to calculate the
CRC
then again to add to theZipOutputStream
was as fast or faster than just processing all the files once asDEFLATED
and changing the.setLevel()
on theZipOutputStream
.There is no local filesystem caching going on with the network access. This is a worse case scenario, processing files on the local disk will be much much faster because of local filesystem caching.
So this hack is a naive approach and is based on false assumptions. It is processing the data through the compression algorithm even at
NO_COMPRESSION
level and the overhead is higher than reading the files twice.