如何使用boto将文件从Amazon S3流转到Rackspace Cloudfiles?

54 投票
6 回答
73624 浏览
提问于 2025-04-17 03:31

我正在把一个文件从S3复制到Cloudfiles,我想避免把文件写到硬盘上。Python-Cloudfiles库里有一个叫做object.stream()的功能,看起来正是我需要的,但我在boto里找不到类似的功能。我希望能做到像下面这样:

shutil.copyfileobj(s3Object.stream(),rsObject.stream())

用boto(或者其他任何S3库)能做到这一点吗?

6 个回答

21

我想看到这个问题的人中,至少有一些人和我一样,想要一种方法来逐行(或者按逗号、或者其他分隔符)读取boto中的文件。这里有一个简单的方法:

def getS3ResultsAsIterator(self, aws_access_info, key, prefix):        
    s3_conn = S3Connection(**aws_access)
    bucket_obj = s3_conn.get_bucket(key)
    # go through the list of files in the key
    for f in bucket_obj.list(prefix=prefix):
        unfinished_line = ''
        for byte in f:
            byte = unfinished_line + byte
            #split on whatever, or use a regex with re.split()
            lines = byte.split('\n')
            unfinished_line = lines.pop()
            for line in lines:
                yield line

@garnaat上面的回答依然很棒,完全正确。希望我的方法也能对某些人有所帮助。

89

这个讨论里的其他回答都和boto有关,但在boto3中,S3.Object已经不能被直接遍历了。所以,下面的代码是不能用的,它会报错:TypeError: 's3.Object' object is not iterable

s3 = boto3.session.Session(profile_name=my_profile).resource('s3')
s3_obj = s3.Object(bucket_name=my_bucket, key=my_key)

with io.FileIO('sample.txt', 'w') as file:
    for i in s3_obj:
        file.write(i)

在boto3中,获取对象内容的方法是通过S3.Object.get()['Body'],这个方法从版本1.9.68开始是可以遍历的,但之前的版本不行。所以,下面的代码在最新版本的boto3中可以用,但在旧版本中不行:

body = s3_obj.get()['Body']
with io.FileIO('sample.txt', 'w') as file:
    for i in body:
        file.write(i)

对于旧版本的boto3,可以使用read方法,但这个方法会把整个S3对象加载到内存中,对于大文件来说,这可能不是个好主意:

body = s3_obj.get()['Body']
with io.FileIO('sample.txt', 'w') as file:
    for i in body.read():
        file.write(i)

不过,read方法可以传入amt参数,指定我们想从流中读取的字节数。这个方法可以多次调用,直到整个流都被读取完:

body = s3_obj.get()['Body']
with io.FileIO('sample.txt', 'w') as file:
    while file.write(body.read(amt=512)):
        pass

深入研究botocore.response.StreamingBody的代码后,我们发现底层的流也是可以访问的,所以我们可以这样遍历:

body = s3_obj.get()['Body']
with io.FileIO('sample.txt', 'w') as file:
    for b in body._raw_stream:
        file.write(b)

在网上搜索时,我也看到了一些可能有用的链接,但我还没尝试过:

23

boto中的Key对象代表S3中的一个对象,可以像迭代器一样使用,所以你可以这样做:

>>> import boto
>>> c = boto.connect_s3()
>>> bucket = c.lookup('garnaat_pub')
>>> key = bucket.lookup('Scan1.jpg')
>>> for bytes in key:
...   write bytes to output stream

或者,像你例子中的情况,你可以这样做:

>>> shutil.copyfileobj(key, rsObject.stream())

撰写回答