Boto3 pour télécharger tous les fichiers d'un seau S3

j'utilise boto3 pour récupérer des fichiers de S3 bucket. J'ai besoin d'une fonctionnalité similaire comme aws s3 sync

Mon code actuel est

#!/usr/bin/python
import boto3
s3=boto3.client('s3')
list=s3.list_objects(Bucket='my_bucket_name')['Contents']
for key in list:
    s3.download_file('my_bucket_name', key['Key'], key['Key'])

cela fonctionne très bien, tant que le seau n'a que des fichiers. Si un dossier est présent dans le seau, son lancer une erreur

Traceback (most recent call last):
  File "./test", line 6, in <module>
    s3.download_file('my_bucket_name', key['Key'], key['Key'])
  File "/usr/local/lib/python2.7/dist-packages/boto3/s3/inject.py", line 58, in download_file
    extra_args=ExtraArgs, callback=Callback)
  File "/usr/local/lib/python2.7/dist-packages/boto3/s3/transfer.py", line 651, in download_file
    extra_args, callback)
  File "/usr/local/lib/python2.7/dist-packages/boto3/s3/transfer.py", line 666, in _download_file
    self._get_object(bucket, key, filename, extra_args, callback)
  File "/usr/local/lib/python2.7/dist-packages/boto3/s3/transfer.py", line 690, in _get_object
    extra_args, callback)
  File "/usr/local/lib/python2.7/dist-packages/boto3/s3/transfer.py", line 707, in _do_get_object
    with self._osutil.open(filename, 'wb') as f:
  File "/usr/local/lib/python2.7/dist-packages/boto3/s3/transfer.py", line 323, in open
    return open(filename, mode)
IOError: [Errno 2] No such file or directory: 'my_folder/.8Df54234'

est-ce une bonne façon de télécharger un seau S3 complet en utilisant boto3. Comment télécharger des dossiers.

37
demandé sur John Rotenstein 2015-08-10 14:58:16

8 réponses

j'ai eu les mêmes besoins et de créer la fonction suivante téléchargement récursivement les fichiers. Les répertoires ne sont créés localement que s'ils contiennent des fichiers.

import boto3
import os

def download_dir(client, resource, dist, local='/tmp', bucket='your_bucket'):
    paginator = client.get_paginator('list_objects')
    for result in paginator.paginate(Bucket=bucket, Delimiter='/', Prefix=dist):
        if result.get('CommonPrefixes') is not None:
            for subdir in result.get('CommonPrefixes'):
                download_dir(client, resource, subdir.get('Prefix'), local, bucket)
        if result.get('Contents') is not None:
            for file in result.get('Contents'):
                if not os.path.exists(os.path.dirname(local + os.sep + file.get('Key'))):
                     os.makedirs(os.path.dirname(local + os.sep + file.get('Key')))
                resource.meta.client.download_file(bucket, file.get('Key'), local + os.sep + file.get('Key'))

La fonction est appelée de cette façon:

def _start():
    client = boto3.client('s3')
    resource = boto3.resource('s3')
    download_dir(client, resource, 'clientconf/', '/tmp')
44
répondu glefait 2017-02-05 19:34:59

Amazon S3 n'a pas de dossiers/répertoires. C'est un structure de fichier plat.

afin De maintenir l'apparence de répertoires, les noms de chemins sont stockés dans la clé d'objet (nom du fichier). Par exemple:

  • images/foo.jpg

dans ce cas, la clé entière est images/foo.jpg plutôt que de simplement les foo.jpg.

je soupçonne que votre problème est que boto retourne un fichier appelé my_folder/.8Df54234 et est tenter de l'enregistrer dans le système de fichiers local. Cependant, votre système de fichiers local interprète le my_folder/ partie comme un nom de répertoire, et ce répertoire n'existe pas sur votre système de fichiers local.

Vous pouvez soit troncature le nom du fichier pour enregistrer seulement le .8Df54234 partie, ou que vous auriez à créer les répertoires nécessaires avant d'écrire des fichiers. Notez qu'il peut s'agir de répertoires imbriqués à plusieurs niveaux.

une voie plus facile serait d'utiliser le AWS Interface de Ligne de Commande (CLI), qui fera ce travail pour vous, par exemple:

aws s3 cp --recursive s3://my_bucket_name local_folder

il y a aussi un sync option qui ne copie que les fichiers nouveaux et modifiés.

28
répondu John Rotenstein 2015-08-10 21:13:18
import os
import boto3

#intiate s3 resource
s3 = boto3.resource('s3')

# select bucket
my_bucket = s3.Bucket('my_bucket_name')

# download file into current directory
for object in my_bucket.objects.all():
    # Need to split object.key into path and file name, else it will give error file not found.
    path, filename = os.path.split(obj.key)
    my_bucket.download_file(object.key, filename))
17
répondu Tushar Niras 2018-10-11 08:36:58

je suis en train d'accomplir la tâche, en utilisant le

#!/usr/bin/python
import boto3
s3=boto3.client('s3')
list=s3.list_objects(Bucket='bucket')['Contents']
for s3_key in list:
    s3_object = s3_key['Key']
    if not s3_object.endswith("/"):
        s3.download_file('bucket', s3_object, s3_object)
    else:
        import os
        if not os.path.exists(s3_object):
            os.makedirs(s3_object)

bien que, il fait le travail, je ne suis pas sûr que ce soit bon de faire de cette façon. Je le laisse ici pour aider les autres utilisateurs et d'autres réponses, avec une meilleure façon d'atteindre ce

8
répondu Shan 2015-08-12 19:50:36

Mieux vaut tard que jamais:) La réponse précédente avec paginator est vraiment bon. Cependant, il est récursif, et vous pourriez finir par frapper Python récursivité limites. Voici une autre approche, avec quelques vérifications supplémentaires.

import os
import errno
import boto3


def assert_dir_exists(path):
    """
    Checks if directory tree in path exists. If not it created them.
    :param path: the path to check if it exists
    """
    try:
        os.makedirs(path)
    except OSError as e:
        if e.errno != errno.EEXIST:
            raise


def download_dir(client, bucket, path, target):
    """
    Downloads recursively the given S3 path to the target directory.
    :param client: S3 client to use.
    :param bucket: the name of the bucket to download from
    :param path: The S3 directory to download.
    :param target: the local directory to download the files to.
    """

    # Handle missing / at end of prefix
    if not path.endswith('/'):
        path += '/'

    paginator = client.get_paginator('list_objects_v2')
    for result in paginator.paginate(Bucket=bucket, Prefix=path):
        # Download each file individually
        for key in result['Contents']:
            # Calculate relative path
            rel_path = key['Key'][len(path):]
            # Skip paths ending in /
            if not key['Key'].endswith('/'):
                local_file_path = os.path.join(target, rel_path)
                # Make sure directories exist
                local_file_dir = os.path.dirname(local_file_path)
                assert_dir_exists(local_file_dir)
                client.download_file(bucket, key['Key'], local_file_path)


client = boto3.client('s3')

download_dir(client, 'bucket-name', 'path/to/data', 'downloads')
3
répondu ifoukarakis 2018-02-06 22:24:58

C'est une très mauvaise idée pour obtenir tous les fichiers en une seule fois, vous devriez plutôt faire dans les lots.

une implémentation que j'utilise pour récupérer un dossier (répertoire) particulier à partir de S3 est,

def get_directory(directory_path, download_path, exclude_file_names):
    # prepare session
    session = Session(aws_access_key_id, aws_secret_access_key, region_name)

    # get instances for resource and bucket
    resource = session.resource('s3')
    bucket = resource.Bucket(bucket_name)

    for s3_key in self.client.list_objects(Bucket=self.bucket_name, Prefix=directory_path)['Contents']:
        s3_object = s3_key['Key']
        if s3_object not in exclude_file_names:
            bucket.download_file(file_path, download_path + str(s3_object.split('/')[-1])

et si vous voulez obtenir le seau de l'utiliser via CIL @John Rotenstein a mentionné comme ci-dessous,

aws s3 cp --recursive s3://bucket_name download_path
1
répondu Ganatra 2017-05-23 12:10:41

j'ai une solution de contournement pour cela qui exécute L'AWS CLI dans le même processus.

Installer awscli comme Python lib:

pip install awscli

alors définissez cette fonction:

from awscli.clidriver import create_clidriver

def aws_cli(*cmd):
    old_env = dict(os.environ)
    try:

        # Environment
        env = os.environ.copy()
        env['LC_CTYPE'] = u'en_US.UTF'
        os.environ.update(env)

        # Run awscli in the same process
        exit_code = create_clidriver().main(*cmd)

        # Deal with problems
        if exit_code > 0:
            raise RuntimeError('AWS CLI exited with code {}'.format(exit_code))
    finally:
        os.environ.clear()
        os.environ.update(old_env)

exécuter:

aws_cli('s3', 'sync', '/path/to/source', 's3://bucket/destination', '--delete')
1
répondu mattalxndr 2018-03-11 06:50:59
for objs in my_bucket.objects.all():
    print(objs.key)
    path='/tmp/'+os.sep.join(objs.key.split(os.sep)[:-1])
    try:
        if not os.path.exists(path):
            os.makedirs(path)
        my_bucket.download_file(objs.key, '/tmp/'+objs.key)
    except FileExistsError as fe:                          
        print(objs.key+' exists')

ce code va télécharger le contenu en /tmp/ répertoire. Si vous voulez, vous pouvez changer le répertoire.

0
répondu Rajesh Rajendran 2018-08-08 11:06:46