【问题标题】:streaming gzipped files from google cloud storage从谷歌云存储流式传输 gzip 文件
【发布时间】:2015-02-08 21:10:20
【问题描述】:

我想直接从 Google Cloud Storage 读取压缩文件并使用 Python csv 包打开它们。 本地文件的代码是:

def reader(self):
    print "reading local compressed file: ", self._filename
    self._localfile = gzip.open(self._filename, 'rb')
    csvReader = csv.reader(self._localfile, delimiter=',', quotechar='"')
    return csvReader

我使用过几个 GCS API(基于 JSON,cloud.storage),但它们似乎都没有给我可以通过 gzip 流式传输的东西。更重要的是,即使文件被解压缩,我也无法打开文件并将其交给 cv.reader(Iterator 类型)。

我压缩的 CSV 文件大约 500MB,而未压缩时它们最多使用几 GB。我不认为这是一个好主意:1 - 在打开文件之前在本地下载文件(除非我可以重叠下载和计算)或 2 - 在计算之前完全在内存中打开它。

最后,我目前在本地机器上运行此代码,但最终我将转移到 AppEngine,所以它也必须在那里工作。

谢谢!!

【问题讨论】:

  • 把你的文件分成多个部分怎么样?
  • 这已经是 1+TB 数据集的多个部分。 :D 进一步打破它似乎是一个不必要的想法。我正在尝试 Alex Martelli 的建议。

标签: python google-app-engine csv google-cloud-storage


【解决方案1】:

使用GCScloudstorage.open(filename, 'r') 将为您提供一个只读的类似文件的对象(之前创建的类似但使用'w':-),您可以一次使用一个块,与标准 Python 库的zlib module,特别是 zlib.decompressobj,当然,如果 GS 对象最初是以互补方式创建的(使用 zlib.compressobj)。

或者,为方便起见,您可以使用标准 Python 库的 gzip module,例如在阅读阶段,例如:

compressed_flo = cloudstorage.open('objname', 'r')
uncompressed_flo = gzip.GzipFile(fileobj=compressed_flo,mode='rb')
csvReader = csv.reader(uncompressed_flo)

当然,对于早期的写作阶段,反之亦然。

请注意,当您在本地运行时(使用 dev_appserver),GCS 客户端库使用 本地磁盘文件 来模拟 GCS ——根据我的经验,这对开发目的很有用,我可以使用 @987654331 @ 或其他工具,当我需要从我的本地工作站与“真实”GCS 存储进行交互时...... GCS 用于当我需要从我的 GAE 应用程序进行这种交互时(以及首先在本地开发所述 GAE 应用程序:-)。

【讨论】:

  • “当然,如果 GS 对象最初是以互补方式创建的(使用 zlib.compressobj)”——为什么会有这个限制?这应该适用于任何有效的 gzip 压缩文件内容,对吧?不是我测试过的,而是 zlib 模块的标题是“zlib — Compression compatible with gzip” ;-).
  • gzip 添加了zlib 不知道也不关心的元数据——之后gzipzlib 压缩数据体,但仍然做了很多元数据工作,例如作为CRC维护。请参阅svn.python.org/projects/python/branches/py3k/Lib/gzip.py 了解gzip.py 的来源——500 多行代码 zlib!-) 当然与GCS 无关——自己转储到/恢复从本地磁盘文件中,您将遇到相同的问题。最好、最简单、最聪明:要么让gzip 双向工作,或者,如果元数据对你来说是死包,zlib 双向工作(节省几个字节)。
  • 我同意,为简单起见,应该以两种方式使用相同的工具。然而,我现在很好奇为什么 zlib 模块声称“压缩与 gzip 兼容”,并查看了 zlib.net 的文档。事实证明,“gzip 添加了 zlib 不知道也不关心的元数据” 并不正确。 zlib 可以自动检测 gzip 包装器(页眉 + 页脚)并且可以忽略它,具体取决于 windowBits 设置。在 Python 中,此功能也通过 zlib.decompressobj()wbits 参数公开。那很有意思。我在答案中添加了示例代码。
  • @Jan-PhilipGehrcke 它在摘要中很有趣,但属于我的“如果元数据对你来说是死包”的条件:是的,你可以比我想象的更容易忽略它,但是,如果你无论如何都忽略它,为什么要先存储它并为其存储付费?-)
  • @Jan-PhilipGehrcke,你说得对,我怀疑该文档的补丁会受到欢迎!
【解决方案2】:

因此,您已将 gzip 文件存储在 GCS 上。您可以以流式方式处理存储在 GCS 上的数据。也就是说,您可以同时下载、解压缩和处理。这避免了

  • 将解压后的文件保存在磁盘上
  • 必须等到下载完成才能处理数据。

gzip 文件有一个小的页眉和页脚,主体是一个压缩流,由一系列块组成,每个块都可以单独解压缩。 Python 的zlib package 可以帮助您!

编辑:这是关于如何解压缩和分析 zlib 或 gzip 流的示例代码,完全基于 zlib

import zlib
from collections import Counter


def stream(filename):
    with open(filename, "rb") as f:
        while True:
            chunk = f.read(1024)
            if not chunk:
                break
            yield chunk


def decompress(stream):
    # Generate decompression object. Auto-detect and ignore
    # gzip wrapper, if present.
    z = zlib.decompressobj(32+15)
    for chunk in stream:
        r = z.decompress(chunk)
        if r:
            yield r


c = Counter()
s = stream("data.gz")
for chunk in decompress(s):
    for byte in chunk:
        c[byte] += 1


print c

我使用示例文件 data.gz 测试了这段代码,该文件是使用 GNU gzip 创建的。

引用http://www.zlib.net/manual.html:

对于可选的 gzip 解码,windowBits 也可以大于 15。添加 32 到 windowBits 以启用 zlib 和 gzip 自动解码 标头检测,或添加 16 以仅解码 gzip 格式(zlib 格式将返回 Z_DATA_ERROR)。如果正在解码 gzip 流, strm->adler 是 crc32 而不是 adler32。

gzip 标头中包含的任何信息都不会保留 [...]

【讨论】:

    猜你喜欢
    • 2020-11-23
    • 1970-01-01
    • 2016-01-13
    • 2018-04-02
    • 2021-12-07
    • 1970-01-01
    • 2020-08-26
    • 2015-02-25
    • 2019-12-10
    相关资源
    最近更新 更多