web-dev-qa-db-fra.com

Comment sauvegarder un objet S3 dans un fichier en utilisant boto3

J'essaie de créer un "bonjour le monde" avec le nouveau boto3 client pour AWS.

Le cas d'utilisation que j'ai est assez simple: obtenir un objet de S3 et l'enregistrer dans le fichier.

Dans boto 2.X je le ferais comme ceci:

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

En boto 3. Je n'arrive pas à trouver un moyen propre de faire la même chose, donc je répète manuellement sur l'objet "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)

ou

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)

Et ça marche bien. Je me demandais s'il y avait une fonction "native" de boto3 qui ferait la même tâche?

98
Vor

Il y a une personnalisation qui a été récemment introduite dans Boto3 et qui aide à cela (entre autres choses). Il est actuellement exposé sur le client S3 de bas niveau et peut être utilisé comme suit:

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

Ces fonctions gèrent automatiquement la lecture/l'écriture de fichiers, ainsi que les téléchargements multipart en parallèle pour les fichiers volumineux.

165
Daniel

boto3 a maintenant une interface plus agréable que le client:

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

En soi, cela n’est pas beaucoup mieux que client dans la réponse acceptée (bien que les docs disent qu’il fait un meilleur travail en essayant de réessayer les téléchargements et les téléchargements en cas d’échec), mais étant donné que les ressources sont généralement plus ergonomiques (par exemple, le s3 bucket Les ressources et object sont plus agréables que les méthodes client). Cela vous permet de rester au niveau de la couche ressource sans avoir à vous déplacer vers le bas.

Resources peuvent généralement être créés de la même manière que les clients. Ils acceptent tous les arguments, ou presque, et les transmettent simplement à leurs clients internes.

53
quodlibetor

Pour ceux d’entre vous qui souhaitent simuler les méthodes set_contents_from_string comme boto2, vous pouvez essayer

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

Pour Python3:

En python3, les deux StringIO et cStringIO ont disparu . Utilisez l’importation StringIO comme:

from io import StringIO

Pour supporter les deux versions:

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

Remarque: Je suppose que vous avez configuré l'authentification séparément. Le code ci-dessous permet de télécharger l’objet unique à partir du compartiment 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

Si vous souhaitez lire un fichier avec une configuration différente de celle par défaut, n'hésitez pas à utiliser directement mpu.aws.s3_download(s3path, destination) ou le code copié-collé:

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