如何在 Python 中流式传输动态构建的 gzip?

How to stream a gzip built on the fly in Python?

我想使用 asyncio 通过网络传输一个大日志文件。我从数据库中检索数据,格式化它,使用 python 的 zlib 压缩它并通过网络流式传输它。

这里基本上是我使用的代码:

@asyncio.coroutine
def logs(requests):
    # ...

    yield from resp.prepare(request)

    # gzip magic number and compression format
    resp.write(b'\x1f\x8b\x08\x00\x00\x00\x00\x00')
    compressor = compressobj()
    for row in rows:
        ip, uid, date, url, answer, volume = row
        NCSA_ROW = '{} {} - [{}] "GET {} HTTP/1.0" {} {}\n'
        row = NCSA_ROW.format(ip, uid, date, url, answer, volume)
        row = row.encode('utf-8')
        data = compressor.compress(row)
        resp.write(data)
    resp.write(compressor.flush())
    return resp

我检索到的文件无法用 gunzip 和 zcat 打开,出现以下错误:

gzip: out.gz: unexpected end of file

您的 gzip header 是错误的(8 个字节而不是 10 个字节),并且您在其后跟一个使用不同 header 和预告片的 zlib 流。即使你有一个正确的 gzip header,如果你有一个原始的 deflate 流而不是 gzip 流,你仍然不会写一个 gzip 预告片。

为正确执行此操作,请勿尝试编写自己的 gzip header。相反,请求 zlib 写入完整的 gzip 流,这将写入正确的 header、压缩数据和尾部。您可以通过向 compressobj().

提供 31wbits 值来做到这一点