【问题标题】:Can gzip compress data without loading it all into memory, i.e. streaming/on-the-fly?gzip 可以压缩数据而不将其全部加载到内存中,即流式传输/即时传输吗?
【发布时间】:2020-12-24 06:58:00
【问题描述】:

是否可以通过一定数量的流来压缩数据,即无需一次将所有压缩数据加载到内存中?

例如,我可以在具有 2gb 内存的机器上 gzip 压缩一个 10gb 的文件吗?

https://docs.python.org/3/library/gzip.html#gzip.compressgzip.compress函数返回gzip的字节,所以必须全部加载到内存中。但是......尚不清楚gzip.open 在内部是如何工作的:压缩后的字节是否会立即全部存储在内存中。 gzip 格式本身是否使实现流式 gzip 变得特别棘手?

[此问题使用 Python 标记,但也欢迎非 Python 答案]

【问题讨论】:

  • 不,gzip 不需要加载所有内容。它被设计为作为流工作。 gzip.open()返回的对象是一个生成器,根据需要返回数据。
  • @Barmar "returns the data":确认一下,我不是在问解压缩(我很确定 gzip can 以流方式解压缩),而是关于压缩.
  • 不要使用compress()decompress() 方法。使用 gzip.open()mode='w' 将写入压缩文件。您还可以使用底层的GzipFile() 类来写入任何文件对象。
  • 或者您可以使用compress(),但您不必一次压缩所有内容。您可以分块读取文件,并分别压缩每个块。但是您可能无法获得那么好的压缩效果。

标签: python compression gzip


【解决方案1】:

您不必一次压缩所有 10gb。您可以分块读取输入数据,并分别压缩每个块,因此不必一次全部放入内存。

chunksize = 100 * 1024 * 1024 # 100 mb chunks
with open("bigfile.txt") as infile:
    while True:
        chunk = infile.read(chunksize)
        if not chunk:
            break
        compressed = gzip.compress(chunk)
        # do something with compressed

如果您正在创建压缩文件,您可以将块直接写入 gzip 文件。

with open("bigfile.txt") as infile, gzip.open("bigfile.txt.gz", "w") as gzipfile:
    while True:
        chunk = infile.read(chunksize)
        if not chunk:
            break
        gzipfile.write(chunk)

【讨论】:

  • 块的连接是否仍然是有效的 gzip,因此客户端可以在不知道它们是如何压缩的情况下解压缩它们?
  • 我想会,但我不确定。试试看。
  • 另一件事要检查:zlip.compressobj
  • 啊 zlib 看起来正是我想要的! (记录了一些内容以避免将整个内容加载到内存中)。谢谢
  • 啊,虽然我看到gzip模块内部使用zlib
【解决方案2】:

[这是基于@Barmar's answer和cmets]

可以实现流式 gzip 压缩。 gzip 模块使用 zlib 来实现流式压缩,并查看 gzip module source,它似乎没有将所有输出字节加载到内存中。

您也可以直接使用 zlib 模块执行此操作,例如使用生成器的小管道:

import zlib

def yield_uncompressed_bytes():
    # In a real case, would yield bytes pulled from the filesystem or the network
    chunk = b'*' * 65000
    for _ in range(0, 10000):
        print('In: ', len(chunk))
        yield chunk

def yield_compressed_bytes(_uncompressed_bytes):
    compress_obj = zlib.compressobj()
    for chunk in _uncompressed_bytes:
        if compressed_bytes := compress_obj.compress(chunk):
            yield compressed_bytes

    if compressed_bytes := compress_obj.flush():
        yield compressed_bytes

uncompressed_bytes = yield_uncompressed_bytes()
compressed_bytes = yield_compressed_bytes(uncompressed_bytes)

for chunk in compressed_bytes:
    # In a real case, could save to the filesystem, or send over the network
    print('Out:', len(chunk))

可以看到In:Out:穿插,说明zlib compressobj确实没有将整个输出存储在内存中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-07
    • 2012-02-08
    • 2013-11-11
    • 1970-01-01
    • 2011-10-23
    • 2023-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多