我正在编写一个Python库,它以流的方式生成ZIP文件。如果zip的成员的未压缩或压缩数据为4GiB或更大,则必须使用原始ZIP格式的特定扩展-zip 64。总是使用它的问题是它的支持较少。所以,如果需要的话,我只想使用zip 64。但是文件是否是zip 64必须在压缩数据之前在zip * 中指定,因此如果是流式传输,则在压缩数据的大小已知之前。
然而,在某些情况下,未压缩数据的大小 * 是 * 已知的。因此,我想根据这个未压缩的大小预测zlib可以输出的 * 最大 * 大小,如果是4GiB或更大,请使用zip 64模式。
换句话说,如果下面的chunks
的总长度是已知的,那么get_compressed
可以产生的最大字节总长度是多少?(我假设这个最大大小取决于level、memLevel和wbits)
import zlib
chunks = (
b'any',
b'iterable',
b'of',
b'bytes',
b'-' * 1000000,
)
def get_compressed(level=9, memLevel=9, wbits=-zlib.MAX_WBITS):
compress_obj = zlib.compressobj(level=level, memLevel=memLevel, wbits=wbits)
for chunk in chunks:
if compressed := compress_obj.compress(chunk):
yield compressed
if compressed := compress_obj.flush():
yield compressed
print('length', len(b''.join(get_compressed())))
这是复杂的事实,Python zlib module's behaviour is not consistent between Python versions。
我认为Java在不知道未压缩数据大小的情况下尝试了一种“自动zip 64模式”,但是libarchive has problems with it。
2条答案
按热度按时间8i9zcol21#
你可以通过压缩一些随机数据来估计它。1000个块的压缩大小,每个块1000字节,具有不同的参数:
对于每个2000字节的2000块:
所以看起来如果你只改变
level
,它的开销大约是0.015%。Attempt This Online!
qvk1mo1f2#
当然,你可以找到这个。但是,您依赖的是zlib特定版本的详细的、未记录的行为。zlib中的Deflate可以被修改或重写,然后你的代码就被破坏了。
即使您有不可压缩数据的确切界限,您仍然可能最终得到标记为需要Zip64的条目,而这些条目不需要。例如,如果数据是可压缩的,但边界将其推过。
此外,如果流拉链是真正的流,则它应该能够接受流输入,在这种情况下,它首先不知道未压缩的大小是多少。所以这没用。
处理流zipper的正确方法是将本地头标记为不需要Zip64。一旦发现它确实需要Zip64,就使用适当的数据描述符,并将中央目录中的条目标记为需要Zip64。如果解压缩器使用的是中央目录,就像大多数解压缩器一样,那么它就有正确的信息。如果unzipper是流式的,那么它必须尝试所有可能的数据描述符 * 无论如何 *,所以本地头声明什么并不重要。