web-dev-qa-db-ja.com

botoを使用してAmazon S3からRackspace Cloudfilesにファイルをストリーミングするにはどうすればよいですか?

S3からCloudfilesにファイルをコピーしていますが、ファイルをディスクに書き込まないようにしたいと思います。 Python-Cloudfilesライブラリには、必要なように見えるobject.stream()呼び出しがありますが、botoで同等の呼び出しを見つけることができません。私は次のようなことができることを願っています:

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

これはbotoで可能ですか(または他のs3ライブラリだと思います)?

31

S3のオブジェクトを表すbotoのKeyオブジェクトは、イテレーターのように使用できるため、次のようなことができるはずです。

>>> 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())
19
garnaat

このスレッドの他の回答はbotoに関連していますが、S3.Objectはboto3ではもう反復できません。したがって、以下は機能しません。_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']で利用できますが、これも反復可能ではないため、以下はまだ機能しません。

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

したがって、代わりにreadメソッドを使用することもできますが、これによりWHOLE 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)
_

グーグルしながら、使用できるリンクもいくつか見ましたが、試したことはありません。

47
smallo

私はこの質問を見ている人の少なくとも一部が私のようになり、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%真実です。うまくいけば、私はまだ誰かを助けます。

20
Eli

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)
2
Vic

これはストリーミング本文をラップする私の解決策です:

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
2
jzhou