it-swarm.com.ru

Как сохранить объект S3 в файл, используя boto3

Я пытаюсь сделать «привет мир» с новым boto3 клиентом для AWS.

У меня довольно простой пример использования: получить объект из S3 и сохранить его в файл.

В boto 2.X я бы сделал это так:

import boto
key = boto.connect_s3().get_bucket('foo').get_key('foo')
key.get_contents_to_filename('/tmp/foo')

В бото 3. Я не могу найти чистый способ сделать то же самое, поэтому я вручную перебираю объект «Streaming»:

import boto3
key = boto3.resource('s3').Object('fooo', 'docker/my-image.tar.gz').get()
with open('/tmp/my-image.tar.gz', 'w') as f:
    chunk = key['Body'].read(1024*8)
    while chunk:
        f.write(chunk)
        chunk = key['Body'].read(1024*8)

или же

import boto3
key = boto3.resource('s3').Object('fooo', 'docker/my-image.tar.gz').get()
with open('/tmp/my-image.tar.gz', 'w') as f:
    for chunk in iter(lambda: key['Body'].read(4096), b''):
        f.write(chunk)

И это прекрасно работает. Мне было интересно, есть ли какая-нибудь "родная" функция boto3, которая будет выполнять ту же задачу?

98
Vor

В Boto3 недавно появилась настройка, которая помогает в этом (помимо всего прочего). В настоящее время он доступен на низкоуровневом клиенте S3 и может использоваться следующим образом:

s3_client = boto3.client('s3')
open('hello.txt').write('Hello, world!')

# Upload the file to S3
s3_client.upload_file('hello.txt', 'MyBucket', 'hello-remote.txt')

# Download the file from S3
s3_client.download_file('MyBucket', 'hello-remote.txt', 'hello2.txt')
print(open('hello2.txt').read())

Эти функции будут автоматически обрабатывать чтение/запись файлов, а также выполнять многоэтапную загрузку параллельно для больших файлов.

165
Daniel

у boto3 теперь более приятный интерфейс, чем у клиента:

resource = boto3.resource('s3')
my_bucket = resource.Bucket('MyBucket')
my_bucket.download_file(key, local_filename)

Само по себе это не намного лучше, чем client в принятом ответе (хотя в документах говорится, что он лучше выполняет повторные попытки загрузки и выгрузки при сбое), но, учитывая, что ресурсы обычно более эргономичны (например, корзина s3 и объект ресурсы лучше, чем клиентские методы) это позволяет вам оставаться на уровне ресурсов без необходимости выпадать.

Resources обычно можно создавать так же, как клиенты, и они принимают все или большинство одинаковых аргументов и просто передают их своим внутренним клиентам.

53
quodlibetor

Для тех из вас, кто хотел бы имитировать set_contents_from_string как методы boto2, вы можете попробовать

import boto3
from cStringIO import StringIO

s3c = boto3.client('s3')
contents = 'My string to save to S3 object'
target_bucket = 'hello-world.by.vor'
target_file = 'data/hello.txt'
fake_handle = StringIO(contents)

# notice if you do fake_handle.read() it reads like a file handle
s3c.put_object(Bucket=target_bucket, Key=target_file, Body=fake_handle.read())

Для Python3:

В python3 и StringIO и cStringIO ушли . Используйте импорт StringIO, например:

from io import StringIO

Для поддержки обеих версий:

try:
   from StringIO import StringIO
except ImportError:
   from io import StringIO
35
cgseller
# Preface: File is json with contents: {'name': 'Android', 'status': 'ERROR'}

import boto3
import io

s3 = boto3.resource(
    's3',
    aws_access_key_id='my_access_id',
    aws_secret_access_key='my_secret_key'
)

obj = s3.Object('my-bucket', 'key-to-file.json')
data = io.BytesIO()
obj.download_fileobj(data)

# object is now a bytes string, Converting it to a dict:
new_dict = json.loads(data.getvalue().decode("utf-8"))

print(new_dict['status']) 
# Should print "Error"
10
Lord Sumner

Примечание: я предполагаю, что вы настроили аутентификацию отдельно. Ниже приведен код для загрузки одного объекта из корзины S3.  

import boto3

#initiate s3 client 
s3 = boto3.resource('s3')

#Download object to the file    
s3.Bucket('mybucket').download_file('hello.txt', '/tmp/hello.txt')
1
Tushar Niras

Если вы хотите прочитать файл с конфигурацией, отличной от конфигурации по умолчанию, не стесняйтесь использовать либо mpu.aws.s3_download(s3path, destination) напрямую, либо скопированный код:

def s3_download(source, destination,
                exists_strategy='raise',
                profile_name=None):
    """
    Copy a file from an S3 source to a local destination.

    Parameters
    ----------
    source : str
        Path starting with s3://, e.g. 's3://bucket-name/key/foo.bar'
    destination : str
    exists_strategy : {'raise', 'replace', 'abort'}
        What is done when the destination already exists?
    profile_name : str, optional
        AWS profile

    Raises
    ------
    botocore.exceptions.NoCredentialsError
        Botocore is not able to find your credentials. Either specify
        profile_name or add the environment variables AWS_ACCESS_KEY_ID,
        AWS_SECRET_ACCESS_KEY and AWS_SESSION_TOKEN.
        See https://boto3.readthedocs.io/en/latest/guide/configuration.html
    """
    exists_strategies = ['raise', 'replace', 'abort']
    if exists_strategy not in exists_strategies:
        raise ValueError('exists_strategy \'{}\' is not in {}'
                         .format(exists_strategy, exists_strategies))
    session = boto3.Session(profile_name=profile_name)
    s3 = session.resource('s3')
    bucket_name, key = _s3_path_split(source)
    if os.path.isfile(destination):
        if exists_strategy is 'raise':
            raise RuntimeError('File \'{}\' already exists.'
                               .format(destination))
        Elif exists_strategy is 'abort':
            return
    s3.Bucket(bucket_name).download_file(key, destination)
0
Martin Thoma