Как я могу использовать boto для потоковой передачи файла из Amazon S3 в Rackspace Cloudfiles? - PullRequest
28 голосов
/ 02 октября 2011

Я копирую файл из S3 в Cloudfiles и хотел бы избежать записи файла на диск.В библиотеке Python-Cloudfiles есть вызов object.stream (), который выглядит как то, что мне нужно, но я не могу найти эквивалентный вызов в boto.Я надеюсь, что смогу сделать что-то вроде:

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

Возможно ли это с помощью boto (или, полагаю, любой другой библиотеки s3)?

Ответы [ 5 ]

46 голосов
/ 17 ноября 2016

Другие ответы в этой теме относятся к 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)

Таким образом, альтернативой является использование метода чтения, но он загружает в память объект ВСЕ 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)

При поиске в Google я также видел некоторые ссылки, которые можно было бы использовать, но я не пробовал:

20 голосов
/ 03 июня 2013

Я полагаю, что, по крайней мере, некоторые из людей, видящих этот вопрос, будут похожи на меня, и им нужен способ потоковой передачи файла из 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% верен. Надеюсь, мой еще кого-нибудь выручит.

19 голосов
/ 02 октября 2011

Объект Key в boto, представляющий объект в S3, можно использовать как итератор, поэтому вы должны иметь возможность сделать что-то вроде этого:Например, вы можете сделать:

>>> shutil.copyfileobj(key, rsObject.stream())
2 голосов
/ 31 августа 2018

У Ботокора 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 голосов
/ 29 ноября 2016

Это мое решение обтекания потокового тела:

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
...