使用boto从S3流式传输.gz文件时出现无限循环

7
我正在尝试使用boto从S3流式传输一个.gz文件,并迭代未压缩的文本文件的行。神秘的是,循环永远不会终止;当整个文件被读取时,迭代会重新开始从文件开头。假设我创建并上传如下的输入文件:
> echo '{"key": "value"}' > foo.json
> gzip -9 foo.json
> aws s3 cp foo.json.gz s3://my-bucket/my-location/

我运行以下Python脚本:

import boto
import gzip

connection = boto.connect_s3()
bucket = connection.get_bucket('my-bucket')
key = bucket.get_key('my-location/foo.json.gz')
gz_file = gzip.GzipFile(fileobj=key, mode='rb')
for line in gz_file:
    print(line)

结果如下:
b'{"key": "value"}\n'
b'{"key": "value"}\n'
b'{"key": "value"}\n'
...forever...

为什么会发生这种情况?我认为我肯定漏掉了某些非常基础的东西。
2个回答

10
啊,Boto。问题是如果你在完全读取一次键之后再调用读取方法,它会重新下载该键(比较一下读取和下一个方法的区别)。
这不是最干净的解决方法,但它可以解决问题:
import boto
import gzip

class ReadOnce(object):
    def __init__(self, k):
        self.key = k
        self.has_read_once = False

   def read(self, size=0):
       if self.has_read_once:
           return b''
       data = self.key.read(size)
       if not data:
           self.has_read_once = True
       return data

connection = boto.connect_s3()
bucket = connection.get_bucket('my-bucket')
key = ReadOnce(bucket.get_key('my-location/foo.json.gz'))
gz_file = gzip.GzipFile(fileobj=key, mode='rb')
for line in gz_file:
    print(line)

+1:这太棒了。事实上,使用您的包装器,我可以直接从压缩的S3对象中读取Pandas DataFrame。谢谢! - Pierre D
不错!这同样适用于CSV下载,对我来说也展示了类似将文件末尾与开头拼接的奇怪行为。很想知道boto为什么会这样做 - 在我看来,这是一个非常大的陷阱。 - killthrush
值得注意的是,当将此解决方案作为上下文管理器运行时,我还需要实现一个close函数。它只是将close委托给self.key.close() - killthrush
我们可以将这个解决方案扩展到 boto3,以10 MiB块读取zip文件吗? - user 923227

0

感谢zweiterlinde提供的精彩见解和优秀答案

我正在寻找一种将压缩的S3对象直接读入Pandas DataFrame的解决方案,使用他的包装器,只需两行即可实现:

with gzip.GzipFile(fileobj=ReadOnce(bucket.get_key('my/obj.tsv.gz')), mode='rb') as f:
    df = pd.read_csv(f, sep='\t')

1
Python不是关于“谁能用最少的代码行数实现”,去读圣经-> python -m this - Javier Buzzi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接