Загрузите папку с s3 с помощью boto3

Используя Boto3 Python SDK, мне удалось загрузить файлы методом bucket.download_file().

Есть ли способ загрузить всю папку?

Возможно дубликат - stackoverflow.com/questions/31918960/…

Yoav Gaudin 11.04.2018 12:10
31
2
38 953
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

быстро и грязно, но работает:

import boto3
import os 

def downloadDirectoryFroms3(bucketName, remoteDirectoryName):
    s3_resource = boto3.resource('s3')
    bucket = s3_resource.Bucket(bucketName) 
    for obj in bucket.objects.filter(Prefix = remoteDirectoryName):
        if not os.path.exists(os.path.dirname(obj.key)):
            os.makedirs(os.path.dirname(obj.key))
        bucket.download_file(obj.key, obj.key) # save to same path

Предполагая, что вы хотите загрузить каталог foo / bar из s3, цикл for будет перебирать все файлы, путь которых начинается с Prefix = foo / bar.

Но вы не указали учетные данные!

Arkady 27.06.2019 10:18

@Arkady Учетные данные устанавливаются в ~ / .aws / credentials или как переменные среды. Вы можете найти дополнительную информацию здесь

Konstantinos Katsantonis 27.06.2019 12:12

Учетные данные можно установить по-разному. См. boto3.amazonaws.com/v1/documentation/api/latest/guide/…

Patrick Pötz 09.10.2019 15:01

вы можете объявить учетные данные aws при создании ресурса s3 следующим образом s3_resource = boto3.resource('s3', aws_access_key_id=access_key, aws_secret_access_key=secret_key)

root 21.01.2020 06:51

Я отправил аналогичный вопрос stackoverflow.com/questions/64226700/… - это ваш предлагаемый ответ, как лучший способ решить мою проблему?

Dinero 06.10.2020 19:21

Ну, почти никогда не бывает абсолютно лучшего :).

Konstantinos Katsantonis 07.10.2020 13:37

чтобы сделать это рекурсивным (для каталогов внутри каталогов), загружайте только файл, если не obj.key.endswith ('/'):

Ioannis Tsiokos 28.10.2020 08:37

Используя boto3, вы можете установить учетные данные aws и загрузить набор данных из S3

import boto3
import os 

# set aws credentials 
s3r = boto3.resource('s3', aws_access_key_id='xxxxxxxxxxxxxxxxx',
    aws_secret_access_key='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
bucket = s3r.Bucket('bucket_name')

# downloading folder 
prefix = 'dirname'
for object in bucket.objects.filter(Prefix = 'dirname'):
    if object.key == prefix:
        os.makedirs(os.path.dirname(object.key), exist_ok=True)
        continue;
    bucket.download_file(object.key, object.key)

Если вы не можете найти ur access_key и secret_access_key, обратитесь к этому страница
Надеюсь, это поможет.
Спасибо.

Лучше не помещать ключи в файл кода. В худшем случае вы можете поместить свои ключи в отдельный защищенный файл и импортировать их. Также можно использовать boto3 без кешированных учетных данных и вместо этого использовать либо s3fs, либо просто полагаться на файл конфигурации (reddit.com/r/aws/comments/73212m/…)

Zach Rieck 28.07.2020 21:02

Немного менее грязная модификация принятого ответа Константиноса Кацантониса:

import boto3
s3 = boto3.resource('s3') # assumes credentials & configuration are handled outside python in .aws directory or environment variables

def download_s3_folder(bucket_name, s3_folder, local_dir=None):
    """
    Download the contents of a folder directory
    Args:
        bucket_name: the name of the s3 bucket
        s3_folder: the folder path in the s3 bucket
        local_dir: a relative or absolute directory path in the local file system
    """
    bucket = s3.Bucket(bucket_name)
    for obj in bucket.objects.filter(Prefix=s3_folder):
        target = obj.key if local_dir is None \
            else os.path.join(local_dir, os.path.relpath(obj.key, s3_folder))
        if not os.path.exists(os.path.dirname(target)):
            os.makedirs(os.path.dirname(target))
        if obj.key[-1] == '/':
            continue
        bucket.download_file(obj.key, target)

Это также загружает вложенные подкаталоги. Мне удалось загрузить каталог с более чем 3000 файлами в нем. Вы найдете другие решения на Boto3 для загрузки всех файлов из S3 Bucket, но я не знаю, лучше ли они.

Другой подход, основанный на ответе @bjc, который использует встроенную библиотеку Path и анализирует s3 uri для вас:

import boto3
from pathlib import Path
from urllib.parse import urlparse

def download_s3_folder(s3_uri, local_dir=None):
    """
    Download the contents of a folder directory
    Args:
        s3_uri: the s3 uri to the top level of the files you wish to download
        local_dir: a relative or absolute directory path in the local file system
    """
    s3 = boto3.resource("s3")
    bucket = s3.Bucket(urlparse(s3_uri).hostname)
    s3_path = urlparse(s3_uri).path.lstrip('/')
    if local_dir is not None:
        local_dir = Path(local_dir)
    for obj in bucket.objects.filter(Prefix=s3_path):
        target = obj.key if local_dir is None else local_dir / Path(obj.key).relative_to(s3_path)
        target.parent.mkdir(parents=True, exist_ok=True)
        if obj.key[-1] == '/':
            continue
        bucket.download_file(obj.key, str(target))

Вышеупомянутые решения хороши и полагаются на S3 Resource.
Следующее решение достигает той же цели, но с применением s3_client.
Возможно, вы сочтете это полезным для себя (я тестировал его, и он работает хорошо).

import boto3
from os import path, makedirs
from botocore.exceptions import ClientError
from boto3.exceptions import S3TransferFailedError

def download_s3_folder(s3_folder, local_dir, aws_access_key_id, aws_secret_access_key, aws_bucket, debug_en):
    """ Download the contents of a folder directory into a local area """

    success = True

    print('[INFO] Downloading %s from bucket %s...' % (s3_folder, aws_bucket))

    def get_all_s3_objects(s3, **base_kwargs):
        continuation_token = None
        while True:
            list_kwargs = dict(MaxKeys=1000, **base_kwargs)
            if continuation_token:
                list_kwargs['ContinuationToken'] = continuation_token
            response = s3.list_objects_v2(**list_kwargs)
            yield from response.get('Contents', [])
            if not response.get('IsTruncated'):
                break
            continuation_token = response.get('NextContinuationToken')

    s3_client = boto3.client('s3',
                             aws_access_key_id=aws_access_key_id,
                             aws_secret_access_key=aws_secret_access_key)

    all_s3_objects_gen = get_all_s3_objects(s3_client, Bucket=aws_bucket)

    for obj in all_s3_objects_gen:
        source = obj['Key']
        if source.startswith(s3_folder):
            destination = path.join(local_dir, source)
            if not path.exists(path.dirname(destination)):
                makedirs(path.dirname(destination))
            try:
                s3_client.download_file(aws_bucket, source, destination)
            except (ClientError, S3TransferFailedError) as e:
                print('[ERROR] Could not download file "%s": %s' % (source, e))
                success = False
            if debug_en:
                print('[DEBUG] Downloading: %s --> %s' % (source, destination))

    return success

Вы также можете использовать cloudpathlib, который для S3 обертывает boto3. Для вашего варианта использования это довольно просто:

from cloudpathlib import CloudPath

cp = CloudPath("s3://bucket/folder/folder2/")
cp.download_to("local_folder")

Другие вопросы по теме