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

vecaoik1  于 2023-02-28  发布在  Python
关注(0)|答案(5)|浏览(186)

我正在将一个文件从S3复制到Cloudfiles,我希望避免将该文件写入磁盘。Python-Cloudfiles库中有一个object.stream()调用,看起来正是我所需要的,但我在boto中找不到一个等效的调用。我希望我能够执行以下操作:

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

这在boto(或者我想其他的s3库)上可能吗?

2uluyalo

2uluyalo1#

此线程中的其他答案与boto相关,但S3.Object在boto 3中不再可迭代。因此,以下内容不起作用,它会产生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)

在boto 3中,对象的内容在S3.Object.get()['Body']中可用,S3.Object.get()['Body']是从1.9.68版本开始的可迭代对象,但以前不是。因此,以下代码适用于boto 3的最新版本,但不适用于更早的版本:

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

因此,旧boto 3版本的替代方法是使用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)

虽然谷歌我也看到了一些链接,可以使用,但我还没有尝试:

e1xvtsh3

e1xvtsh32#

boto中的Key对象表示S3中的on对象,可以像迭代器一样使用,所以你应该可以做如下的事情:

>>> 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())
gopyfrb3

gopyfrb33#

我想至少有一些人看到这个问题会像我一样,希望有一种方法可以逐行(或逐逗号,或任何其他分隔符)从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上面的回答仍然很棒,100%正确。希望我的回答仍然能帮助一些人。

uyto3xhc

uyto3xhc4#

Botocore的StreamingBody有一个iter_lines()方法:
https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html#botocore.response.StreamingBody.iter_lines
因此:

import boto3
s3r = boto3.resource('s3')
iterator = s3r.Object(bucket, key).get()['Body'].iter_lines()

for line in iterator:
    print(line)
0ve6wy6x

0ve6wy6x5#

这是我的解决方案 Package 流式正文:

import io
class S3ObjectInterator(io.RawIOBase):
    def __init__(self, bucket, key):
        """Initialize with S3 bucket and key names"""
        self.s3c = boto3.client('s3')
        self.obj_stream = self.s3c.get_object(Bucket=bucket, Key=key)['Body']

    def read(self, n=-1):
        """Read from the stream"""
        return self.obj_stream.read() if n == -1 else self.obj_stream.read(n)

示例用法:

obj_stream = S3ObjectInterator(bucket, key)
for line in obj_stream:
    print line

相关问题