Boto3, aby pobrać wszystkie pliki z wiadra S3

84

Używam boto3 do pobierania plików z zasobnika s3. Potrzebuję podobnej funkcjonalności jakaws s3 sync

Mój obecny kod to

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

Działa to dobrze, o ile zasobnik zawiera tylko pliki. Jeśli folder jest obecny w zasobniku, generuje błąd

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'

Czy to właściwy sposób na pobranie pełnego zasobnika s3 przy użyciu boto3. Jak pobierać foldery.

Shan
źródło

Odpowiedzi:

40

Podczas pracy z zasobnikami, które mają ponad 1000 obiektów, konieczne jest zaimplementowanie rozwiązania wykorzystującego NextContinuationTokensekwencyjne zestawy maksymalnie 1000 kluczy. To rozwiązanie najpierw kompiluje listę obiektów, a następnie iteracyjnie tworzy określone katalogi i pobiera istniejące obiekty.

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)
Grant Langseth
źródło
zmieniając to na akceptowaną odpowiedź, ponieważ obsługuje szerszy przypadek użycia. Dzięki Grant
Shan
mój kod przechodzi w nieskończoną pętlę o godziniewhile next_token is not None:
gpd
@gpd nie powinno to mieć miejsca, ponieważ klient boto3 zwróci stronę bez NextContinuationToken po osiągnięciu ostatniej strony, wychodząc z instrukcji while. Jeśli wkleisz ostatnią odpowiedź, którą uzyskasz za pomocą interfejsu API boto3 (cokolwiek jest przechowywane w zmiennej odpowiedzi), myślę, że będzie bardziej jasne, co dzieje się w twoim konkretnym przypadku. Spróbuj wydrukować zmienną „wyniki” tylko w celu przetestowania. Domyślam się, że podałeś obiekt prefiksu, który nie pasuje do żadnej zawartości twojego zasobnika. Sprawdziłeś to?
Grant Langseth
1
Pamiętaj, że potrzebujesz drobnych zmian, aby działał z Digital Ocean. jak wyjaśniono tutaj
David D.
2
Używając tego kodu, otrzymuję ten błąd: Obiekt „NoneType” nie jest iterowalny: TypeError
NJones
76

Mam te same potrzeby i stworzyłem następującą funkcję, która rekurencyjnie pobiera pliki.

Katalogi są tworzone lokalnie tylko wtedy, gdy zawierają pliki.

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)

Funkcja nazywa się w ten sposób:

def _start():
    client = boto3.client('s3')
    resource = boto3.resource('s3')
    download_dir(client, resource, 'clientconf/', '/tmp', bucket='my-bucket')
glefait
źródło
6
Myślę, że nie musisz tworzyć zasobu i klienta. Uważam, że klient jest zawsze dostępny w zasobie. Możesz po prostu użyć resource.meta.client.
TheHerk,
2
Myślę, że powinno to być „download_dir (client, resource, subdir.get ('Prefix'), local, bucket )”
rm999
6
Otrzymałem komunikat, OSError: [Errno 21] Is a directorywięc zawarłem wywołanie download_file, if not file.get('Key').endswith('/')aby rozwiązać. Dziękuję @glefait i @Shan
user336828
5
Czy aws s3 syncw bibliotece boto3 nie ma odpowiednika polecenia aws-cli ?
greperror
8
Co disttu jest ?
Rob Rose,
49

Amazon S3 nie ma folderów / katalogów. Jest to płaska struktura plików .

Aby zachować wygląd katalogów, nazwy ścieżek są przechowywane jako część obiektu Klucz (nazwa pliku). Na przykład:

  • images/foo.jpg

W tym przypadku cały klucz jest images/foo.jpg, a nie tylko foo.jpg.

Podejrzewam, że twój problem polega na tym, że botozwraca plik o nazwie my_folder/.8Df54234i próbuje zapisać go w lokalnym systemie plików. Jednak lokalny system plików interpretuje tę my_folder/część jako nazwę katalogu, a ten katalog nie istnieje w lokalnym systemie plików .

Możesz albo skrócić nazwę pliku, aby zapisać tylko .8Df54234część, albo musiałbyś utworzyć niezbędne katalogi przed zapisaniem plików. Zauważ, że mogą to być zagnieżdżone katalogi wielopoziomowe.

Łatwiejszym sposobem byłoby skorzystanie z interfejsu wiersza poleceń AWS (CLI) , który wykona całą tę pracę za Ciebie, np .:

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

Istnieje również syncopcja, która kopiuje tylko nowe i zmodyfikowane pliki.

John Rotenstein
źródło
1
@j Rozumiem to. Ale potrzebowałem, aby folder został utworzony automatycznie, tak jak aws s3 sync. Czy jest to możliwe w boto3.
Shan,
4
Musiałbyś uwzględnić utworzenie katalogu jako część kodu Pythona. Jeśli klucz zawiera katalog (np. foo/bar.txt), Będziesz odpowiedzialny za utworzenie katalogu ( foo) przed wywołaniem s3.download_file. Nie jest to automatyczna możliwość boto.
John Rotenstein,
Tutaj zawartość wiadra S3 jest dynamiczna, więc muszę sprawdzić s3.list_objects(Bucket='my_bucket_name')['Contents']i przefiltrować klucze folderów i je utworzyć.
Shan
2
Po dłuższej zabawie z Boto3, polecenie AWS CLI wymienione tutaj jest zdecydowanie najłatwiejszym sposobem na zrobienie tego.
AdjunctProfessorFalcon
1
@Ben Zacznij nowe pytanie, zamiast zadawać pytanie jako komentarz do starego (2015) pytania.
John Rotenstein,
43
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)
Tushar Niras
źródło
3
Czysty i prosty, czy jest jakiś powód, dla którego nie chcesz tego użyć? Jest to o wiele bardziej zrozumiałe niż wszystkie inne rozwiązania. Wydaje się, że kolekcje robią wiele rzeczy w tle.
Joost
3
Myślę, że powinieneś najpierw utworzyć wszystkie podfoldery, aby to działało poprawnie.
rpanai
2
Ten kod umieści wszystko w katalogu wyjściowym najwyższego poziomu, niezależnie od tego, jak głęboko jest on zagnieżdżony w S3. A jeśli wiele plików ma tę samą nazwę w różnych katalogach, będą one tupać jeden z drugim. Myślę, że potrzebujesz jeszcze jednej linii:, os.makedirs(path)a następnie powinno być miejsce docelowe pobierania object.key.
Scott Smith
13

Obecnie realizuję to zadanie, korzystając z poniższych

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

Chociaż spełnia swoje zadanie, nie jestem pewien, czy dobrze jest robić w ten sposób. Zostawiam to tutaj, aby pomóc innym użytkownikom i uzyskać dalsze odpowiedzi, z lepszym sposobem osiągnięcia tego

Shan
źródło
9

Lepiej późno niż wcale :) Poprzednia odpowiedź z paginatorem jest naprawdę dobra. Jednak jest rekurencyjny i może skończyć się osiągnięciem limitów rekurencji Pythona. Oto alternatywne podejście z kilkoma dodatkowymi kontrolami.

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')
ifoukarakis
źródło
1
Mam KeyError: 'Contents'. ścieżka wejściowa '/arch/R/storeincomelogs/, pełna ścieżka /arch/R/storeincomelogs/201901/01/xxx.parquet.
Mithril
3

Mam obejście tego problemu, które uruchamia interfejs wiersza poleceń AWS w tym samym procesie.

Zainstaluj awsclijako python lib:

pip install awscli

Następnie zdefiniuj tę funkcję:

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)

Wykonać:

aws_cli('s3', 'sync', '/path/to/source', 's3://bucket/destination', '--delete')
mattalxndr
źródło
Użyłem tego samego pomysłu, ale bez użycia syncpolecenia, a raczej po prostu wykonałem polecenie aws s3 cp s3://{bucket}/{folder} {local_folder} --recursive. Czas skrócił się z minut (prawie 1h) do dosłownie sekund
acaruci
Używam tego kodu, ale mam problem z wyświetlaniem wszystkich dzienników debugowania. Mam to zadeklarowane globalnie: logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.WARNING) logger = logging.getLogger()i chcę, aby dzienniki były wyprowadzane tylko z roota. Jakieś pomysły?
kwiecień Polubiec
1

Bardzo złym pomysłem jest pobieranie wszystkich plików za jednym zamachem, lepiej jest pobierać je partiami.

Jedną z implementacji, której używam do pobierania określonego folderu (katalogu) z S3 jest:

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

a mimo to, jeśli chcesz uzyskać cały wiadro, użyj go przez CIL, jak wspomniany poniżej @John Rotenstein ,

aws s3 cp --recursive s3://bucket_name download_path
Ganatra
źródło
0
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')

Ten kod pobierze zawartość w /tmp/katalogu. Jeśli chcesz, możesz zmienić katalog.

Rajesh Rajendran
źródło
0

Jeśli chcesz wywołać skrypt bash za pomocą Pythona, oto prosta metoda załadowania pliku z folderu w zasobniku S3 do folderu lokalnego (na komputerze z systemem 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")
HazimoRa3d
źródło
0

Otrzymałem podobny wymóg i otrzymałem pomoc, czytając kilka z powyższych rozwiązań i na innych stronach internetowych, wymyśliłem poniższy skrypt, po prostu chciałem się nim podzielić, jeśli może to komuś pomóc.

from boto3.session import Session
import os

def sync_s3_folder(access_key_id,secret_access_key,bucket_name,folder,destination_path):    
    session = Session(aws_access_key_id=access_key_id,aws_secret_access_key=secret_access_key)
    s3 = session.resource('s3')
    your_bucket = s3.Bucket(bucket_name)
    for s3_file in your_bucket.objects.all():
        if folder in s3_file.key:
            file=os.path.join(destination_path,s3_file.key.replace('/','\\'))
            if not os.path.exists(os.path.dirname(file)):
                os.makedirs(os.path.dirname(file))
            your_bucket.download_file(s3_file.key,file)
sync_s3_folder(access_key_id,secret_access_key,bucket_name,folder,destination_path)
Kranti
źródło
0

Ponowne zamieszczenie odpowiedzi @glefait z warunkiem if na końcu, aby uniknąć błędu systemu operacyjnego 20. Pierwszym otrzymanym kluczem jest sama nazwa folderu, której nie można zapisać w ścieżce docelowej.

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', []):
            print("Content: ",result)
            dest_pathname = os.path.join(local, file.get('Key'))
            print("Dest path: ",dest_pathname)
            if not os.path.exists(os.path.dirname(dest_pathname)):
                print("here last if")
                os.makedirs(os.path.dirname(dest_pathname))
            print("else file key: ", file.get('Key'))
            if not file.get('Key') == dist:
                print("Key not equal? ",file.get('Key'))
                resource.meta.client.download_file(bucket, file.get('Key'), dest_pathname)enter code here
Vinay
źródło
0

Od jakiegoś czasu napotykam ten problem i na wszystkich różnych forach, na których przeglądałem, nie widziałem pełnego, kompleksowego wycinka tego, co działa. Więc poszedłem naprzód i wziąłem wszystkie elementy (dodaj trochę rzeczy samodzielnie) i stworzyłem pełny, kompleksowy program do pobierania S3!

Spowoduje to nie tylko automatyczne pobranie plików, ale jeśli pliki S3 znajdują się w podkatalogach, utworzy je w pamięci lokalnej. W instancji mojej aplikacji muszę ustawić uprawnienia i właścicieli, więc też to dodałem (można to skomentować, jeśli nie jest to potrzebne).

Zostało to przetestowane i działa w środowisku Docker (K8), ale dodałem zmienne środowiskowe w skrypcie na wypadek, gdybyś chciał przetestować / uruchomić go lokalnie.

Mam nadzieję, że pomoże to komuś w poszukiwaniu automatyzacji S3 Download. Z zadowoleniem przyjmuję również wszelkie porady, informacje itp., Jak można to lepiej zoptymalizować w razie potrzeby.

#!/usr/bin/python3
import gc
import logging
import os
import signal
import sys
import time
from datetime import datetime

import boto
from boto.exception import S3ResponseError
from pythonjsonlogger import jsonlogger

formatter = jsonlogger.JsonFormatter('%(message)%(levelname)%(name)%(asctime)%(filename)%(lineno)%(funcName)')

json_handler_out = logging.StreamHandler()
json_handler_out.setFormatter(formatter)

#Manual Testing Variables If Needed
#os.environ["DOWNLOAD_LOCATION_PATH"] = "some_path"
#os.environ["BUCKET_NAME"] = "some_bucket"
#os.environ["AWS_ACCESS_KEY"] = "some_access_key"
#os.environ["AWS_SECRET_KEY"] = "some_secret"
#os.environ["LOG_LEVEL_SELECTOR"] = "DEBUG, INFO, or ERROR"

#Setting Log Level Test
logger = logging.getLogger('json')
logger.addHandler(json_handler_out)
logger_levels = {
    'ERROR' : logging.ERROR,
    'INFO' : logging.INFO,
    'DEBUG' : logging.DEBUG
}
logger_level_selector = os.environ["LOG_LEVEL_SELECTOR"]
logger.setLevel(logger_level_selector)

#Getting Date/Time
now = datetime.now()
logger.info("Current date and time : ")
logger.info(now.strftime("%Y-%m-%d %H:%M:%S"))

#Establishing S3 Variables and Download Location
download_location_path = os.environ["DOWNLOAD_LOCATION_PATH"]
bucket_name = os.environ["BUCKET_NAME"]
aws_access_key_id = os.environ["AWS_ACCESS_KEY"]
aws_access_secret_key = os.environ["AWS_SECRET_KEY"]
logger.debug("Bucket: %s" % bucket_name)
logger.debug("Key: %s" % aws_access_key_id)
logger.debug("Secret: %s" % aws_access_secret_key)
logger.debug("Download location path: %s" % download_location_path)

#Creating Download Directory
if not os.path.exists(download_location_path):
    logger.info("Making download directory")
    os.makedirs(download_location_path)

#Signal Hooks are fun
class GracefulKiller:
    kill_now = False
    def __init__(self):
        signal.signal(signal.SIGINT, self.exit_gracefully)
        signal.signal(signal.SIGTERM, self.exit_gracefully)
    def exit_gracefully(self, signum, frame):
        self.kill_now = True

#Downloading from S3 Bucket
def download_s3_bucket():
    conn = boto.connect_s3(aws_access_key_id, aws_access_secret_key)
    logger.debug("Connection established: ")
    bucket = conn.get_bucket(bucket_name)
    logger.debug("Bucket: %s" % str(bucket))
    bucket_list = bucket.list()
#    logger.info("Number of items to download: {0}".format(len(bucket_list)))

    for s3_item in bucket_list:
        key_string = str(s3_item.key)
        logger.debug("S3 Bucket Item to download: %s" % key_string)
        s3_path = download_location_path + "/" + key_string
        logger.debug("Downloading to: %s" % s3_path)
        local_dir = os.path.dirname(s3_path)

        if not os.path.exists(local_dir):
            logger.info("Local directory doesn't exist, creating it... %s" % local_dir)
            os.makedirs(local_dir)
            logger.info("Updating local directory permissions to %s" % local_dir)
#Comment or Uncomment Permissions based on Local Usage
            os.chmod(local_dir, 0o775)
            os.chown(local_dir, 60001, 60001)
        logger.debug("Local directory for download: %s" % local_dir)
        try:
            logger.info("Downloading File: %s" % key_string)
            s3_item.get_contents_to_filename(s3_path)
            logger.info("Successfully downloaded File: %s" % s3_path)
            #Updating Permissions
            logger.info("Updating Permissions for %s" % str(s3_path))
#Comment or Uncomment Permissions based on Local Usage
            os.chmod(s3_path, 0o664)
            os.chown(s3_path, 60001, 60001)
        except (OSError, S3ResponseError) as e:
            logger.error("Fatal error in s3_item.get_contents_to_filename", exc_info=True)
            # logger.error("Exception in file download from S3: {}".format(e))
            continue
        logger.info("Deleting %s from S3 Bucket" % str(s3_item.key))
        s3_item.delete()

def main():
    killer = GracefulKiller()
    while not killer.kill_now:
        logger.info("Checking for new files on S3 to download...")
        download_s3_bucket()
        logger.info("Done checking for new files, will check in 120s...")
        gc.collect()
        sys.stdout.flush()
        time.sleep(120)
if __name__ == '__main__':
    main()
Towarzysz 35
źródło
0

Z dokumentacji AWS S3 (Jak używać folderów w zasobniku S3?):

W Amazon S3 zasobniki i obiekty są podstawowymi zasobami, a obiekty są przechowywane w zasobnikach. Amazon S3 ma płaską strukturę zamiast hierarchii, jak w systemie plików. Jednak ze względu na prostotę organizacyjną konsola Amazon S3 obsługuje koncepcję folderów jako sposobu grupowania obiektów. Amazon S3 robi to, używając przedrostka nazwy współdzielonej dla obiektów (to znaczy, że obiekty mają nazwy zaczynające się od wspólnego łańcucha). Nazwy obiektów są również nazywane nazwami kluczy.

Na przykład, możesz utworzyć na konsoli folder o nazwie photos i przechowywać w nim obiekt o nazwie myphoto.jpg. Obiekt jest następnie zapisywany z nazwą klucza photos / myphoto.jpg, gdzie zdjęcia / to przedrostek.

Aby pobrać wszystkie pliki z „mybucket” do bieżącego katalogu, przestrzegając emulowanej struktury katalogów zasobnika (tworząc foldery z zasobnika, jeśli nie istnieją jeszcze lokalnie):

import boto3
import os

bucket_name = "mybucket"
s3 = boto3.client("s3")
objects = s3.list_objects(Bucket = bucket_name)["Contents"]
for s3_object in objects:
    s3_key = s3_object["Key"]
    path, filename = os.path.split(s3_key)
    if len(path) != 0 and not os.path.exists(path):
        os.makedirs(path)
    if not s3_key.endswith("/"):
        download_to = path + '/' + filename if path else filename
        s3.download_file(bucket_name, s3_key, download_to)
Daria
źródło
Byłoby lepiej, gdybyś mógł dołączyć wyjaśnienie swojego kodu.
jan
1
@johan, dzięki za informację zwrotną! Dodałem odpowiednie wyjaśnienie
Daria