Cómo guardar un objeto S3 en un archivo usando boto3


Estoy tratando de hacer un "hola mundo" con el nuevo cliente boto3 para AWS.

El caso de uso que tengo es bastante simple: obtener objeto de S3 y guardarlo en el archivo.

En boto 2.X Lo haría así:

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

En boto 3 . No puedo encontrar una manera limpia de hacer lo mismo, así que estoy iterando manualmente sobre el objeto "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)

O

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)

Y funciona bien. Me preguntaba si hay alguna función boto3 "nativa" que haga lo mismo la tarea?

Author: Vor, 2015-04-01

6 answers

Hay una personalización que entró en Boto3 recientemente que ayuda con esto (entre otras cosas). Actualmente está expuesto en el cliente S3 de bajo nivel, y se puede usar así:

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

Estas funciones manejarán automáticamente los archivos de lectura/escritura, así como las cargas de varias partes en paralelo para archivos grandes.

 156
Author: Daniel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-03 10:12:05

Boto3 ahora tiene una interfaz más agradable que el cliente:

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

Esto por sí mismo no es tremendamente mejor que client en la respuesta aceptada (aunque los documentos dicen que hace un mejor trabajo volviendo a intentar las cargas y descargas en caso de error), pero teniendo en cuenta que los recursos son generalmente más ergonómicos (por ejemplo, los recursos s3 bucket y object son más agradables que los métodos cliente) esto le permite permanecer en la capa de recursos sin tener que soltar abajo.

Resources generalmente se pueden crear de la misma manera que los clientes, y toman todos o la mayoría de los mismos argumentos y simplemente los reenvían a sus clientes internos.

 47
Author: quodlibetor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-23 20:48:45

Para aquellos de ustedes que quieran simular los métodos set_contents_from_string como boto2, pueden probar

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

Para Python3:

En python3 tanto StringIO como cStringIO se han ido. Usa la importación StringIO como:

from io import StringIO

Para soportar ambas versiones:

try:
   from StringIO import StringIO
except ImportError:
   from io import StringIO
 35
Author: cgseller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-12 03:51:17
# 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"
 8
Author: SixDays,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-24 15:26:24

Nota: Asumo que ha configurado la autenticación por separado. El siguiente código es para descargar el único objeto del bucket 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')
 0
Author: Tushar Niras,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-18 23:49:00

Cuando desee leer un archivo con una configuración diferente a la predeterminada, no dude en usar cualquiera de los dos mpu.aws.s3_download(s3path, destination) directamente o el código copiado:

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
Author: Martin Thoma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-23 19:35:47