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