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?
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.
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.
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
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"
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')
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)
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