list_objects example create aws python amazon-web-services amazon-s3 boto3

example - python s3 bucket



Boto3 para descargar todos los archivos de un S3 Bucket (10)

Estoy usando boto3 para obtener archivos del cubo s3. Necesito una funcionalidad similar como aws s3 sync

Mi código actual es

#!/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''])

Esto funciona bien, siempre que el depósito solo tenga archivos. Si hay una carpeta dentro del cubo, arroja un error

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

¿Es esta una forma adecuada de descargar un bucket completo de s3 usando boto3? Cómo descargar carpetas.


Actualmente estoy logrando la tarea, usando lo siguiente

#!/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)

Aunque hace el trabajo, no estoy seguro de que sea bueno hacerlo de esta manera. Lo dejo aquí para ayudar a otros usuarios y otras respuestas, con una mejor manera de lograr esto


Amazon S3 no tiene carpetas / directorios. Es una estructura de archivo plano .

Para mantener la apariencia de los directorios, los nombres de ruta se almacenan como parte de la clave del objeto (nombre de archivo). Por ejemplo:

  • images/foo.jpg

En este caso, la clave completa es images/foo.jpg , en lugar de solo foo.jpg .

Sospecho que su problema es que boto está devolviendo un archivo llamado my_folder/.8Df54234 e intenta guardarlo en el sistema de archivos local. Sin embargo, su sistema de archivos local interpreta my_folder/ porción como un nombre de directorio, y ese directorio no existe en su sistema de archivos local .

Puede truncar el nombre de archivo para guardar solo la porción .8Df54234 , o tendría que crear los directorios necesarios antes de escribir archivos. Tenga en cuenta que podrían ser directorios anidados de varios niveles.

Una forma más fácil sería utilizar la interfaz de línea de comandos (CLI) de AWS , que hará todo este trabajo por usted, por ejemplo:

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

También hay una opción de sync que solo copiará archivos nuevos y modificados.


Cuando se trabaja con cubos que tienen más de 1000 objetos, es necesario implementar una solución que use NextContinuationToken en conjuntos secuenciales de, como máximo, 1000 claves. Esta solución primero compila una lista de objetos, luego crea iterativamente los directorios especificados y descarga los objetos existentes.

import boto3 import os s3_client = boto3.client(''s3'') def download_dir(prefix, local, bucket, client=s3_client): """ params: - prefix: pattern to match in s3 - local: local path to folder in which to place files - bucket: s3 bucket with target contents - client: initialized s3 client object """ keys = [] dirs = [] next_token = '''' base_kwargs = { ''Bucket'':bucket, ''Prefix'':prefix, } while next_token is not None: kwargs = base_kwargs.copy() if next_token != '''': kwargs.update({''ContinuationToken'': next_token}) results = client.list_objects_v2(**kwargs) contents = results.get(''Contents'') for i in contents: k = i.get(''Key'') if k[-1] != ''/'': keys.append(k) else: dirs.append(k) next_token = results.get(''NextContinuationToken'') for d in dirs: dest_pathname = os.path.join(local, d) if not os.path.exists(os.path.dirname(dest_pathname)): os.makedirs(os.path.dirname(dest_pathname)) for k in keys: dest_pathname = os.path.join(local, k) if not os.path.exists(os.path.dirname(dest_pathname)): os.makedirs(os.path.dirname(dest_pathname)) client.download_file(bucket, k, dest_pathname)


Es una muy mala idea obtener todos los archivos de una vez, más bien debería obtenerlos en lotes.

Una implementación que uso para recuperar una carpeta (directorio) particular de S3 es,

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

y aún así, si desea obtener el cubo completo, share través de CIL como share continuación,

aws s3 cp --recursive s3://bucket_name download_path


Más vale tarde que nunca :) La respuesta anterior con paginator es realmente buena. Sin embargo, es recursivo y podrías terminar alcanzando los límites de recursión de Python. Aquí hay un enfoque alternativo, con un par de controles adicionales.

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


Si desea llamar a un script bash usando python, aquí hay un método simple para cargar un archivo desde una carpeta en el cubo S3 a una carpeta local (en una máquina Linux):

import boto3 import subprocess import os ###TOEDIT### my_bucket_name = "your_my_bucket_name" bucket_folder_name = "your_bucket_folder_name" local_folder_path = "your_local_folder_path" ###TOEDIT### # 1.Load thes list of files existing in the bucket folder FILES_NAMES = [] s3 = boto3.resource(''s3'') my_bucket = s3.Bucket(''{}''.format(my_bucket_name)) for object_summary in my_bucket.objects.filter(Prefix="{}/".format(bucket_folder_name)): # print(object_summary.key) FILES_NAMES.append(object_summary.key) # 2.List only new files that do not exist in local folder (to not copy everything!) new_filenames = list(set(FILES_NAMES )-set(os.listdir(local_folder_path))) # 3.Time to load files in your destination folder for new_filename in new_filenames: upload_S3files_CMD = """aws s3 cp s3://{}/{}/{} {}""".format(my_bucket_name,bucket_folder_name,new_filename ,local_folder_path) subprocess_call = subprocess.call([upload_S3files_CMD], shell=True) if subprocess_call != 0: print("ALERT: loading files not working correctly, please re-check new loaded files")


Tengo las mismas necesidades y creé la siguiente función que descarga recursivamente los archivos.

Los directorios se crean localmente solo si contienen archivos.

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) for file in result.get(''Contents'', []): dest_pathname = os.path.join(local, file.get(''Key'')) if not os.path.exists(os.path.dirname(dest_pathname)): os.makedirs(os.path.dirname(dest_pathname)) resource.meta.client.download_file(bucket, file.get(''Key''), dest_pathname)

La función se llama así:

def _start(): client = boto3.client(''s3'') resource = boto3.resource(''s3'') download_dir(client, resource, ''clientconf/'', ''/tmp'', bucket=''my-bucket'')


Tengo una solución alternativa para esto que ejecuta AWS CLI en el mismo proceso.

Instale awscli como python lib:

pip install awscli

Luego defina esta función:

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)

Ejecutar:

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

Este código descargará el contenido en el directorio /tmp/ . Si quieres puedes cambiar el directorio.


import os import boto3 #initiate s3 resource s3 = boto3.resource(''s3'') # select bucket my_bucket = s3.Bucket(''my_bucket_name'') # download file into current directory for s3_object in my_bucket.objects.all(): # Need to split s3_object.key into path and file name, else it will give error file not found. path, filename = os.path.split(s3_object.key) my_bucket.download_file(s3_object.key, filename)