Pobieranie nazw podfolderów w zasobniku S3 z boto3


85

Korzystając z boto3, mogę uzyskać dostęp do mojego wiadra AWS S3:

s3 = boto3.resource('s3')
bucket = s3.Bucket('my-bucket-name')

Teraz zasobnik zawiera folder first-level, który sam zawiera na przykład kilka podfolderów o nazwach ze znacznikiem czasu 1456753904534. Muszę znać nazwy tych podfolderów do innej pracy, którą wykonuję i zastanawiam się, czy mógłbym poprosić boto3 o ich odzyskanie.

Więc spróbowałem:

objs = bucket.meta.client.list_objects(Bucket='my-bucket-name')

co daje słownik, którego klucz „Zawartość” daje mi wszystkie pliki trzeciego poziomu zamiast katalogów z datownikami drugiego poziomu, w rzeczywistości otrzymuję listę zawierającą rzeczy takie jak

{u'ETag ':' "etag" ', u'Key': first-level / 1456753904534 / part-00014 ', u'LastModified': datetime.datetime (2016, 2, 29, 13, 52, 24, tzinfo = tzutc ()),
u'Owner ': {u'DisplayName': 'owner', u'ID ':' id '},
u'Size': size, u'StorageClass ':' storageclass '}

widać, że w tym przypadku part-00014pobierane są określone pliki, podczas gdy chciałbym uzyskać samą nazwę katalogu. W zasadzie mógłbym usunąć nazwę katalogu ze wszystkich ścieżek, ale pobieranie wszystkiego na trzecim poziomie, aby uzyskać drugi poziom, jest brzydkie i kosztowne!

Próbowałem też czegoś zgłoszonego tutaj :

for o in bucket.objects.filter(Delimiter='/'):
    print(o.key)

ale nie otrzymuję folderów na pożądanym poziomie.

Czy jest sposób na rozwiązanie tego problemu?


Więc mówisz, że to nie działa? Czy możesz opublikować, co się stanie, gdy to uruchomisz?
Jordon Phillips

1
@JordonPhillips Wypróbowałem pierwsze wiersze tego linku, który wysłałeś, który wkleiłem tutaj, i otrzymałem pliki tekstowe na pierwszym poziomie zasobnika i bez folderów.
mar tin

@mar tin Czy kiedykolwiek rozwiązałeś ten problem. Stoję przed podobnym dylematem, w którym potrzebuję pierwszego elementu w każdym podfolderze Buckets.
Ted Taylor of Life,

1
@TedTaylorofLife Yea, nie ma innego sposobu niż zdobycie wszystkich obiektów i podzielenie się nimi, /aby uzyskać podfoldery
mar tin

1
@ mar tin Jedyny sposób, jaki zrobiłem, to pobranie wyniku, wrzucenie go do formatu tekstowego i rozdzielenie przecinkami za pomocą „/”, a następnie skopiowanie i wklejenie pierwszego elementu. Co za wrzód na dupie.
Ted Taylor of Life,

Odpowiedzi:


56

S3 to magazyn obiektowy, nie ma rzeczywistej struktury katalogów. Znak „/” jest raczej kosmetyczny. Jednym z powodów, dla których ludzie chcą mieć strukturę katalogów, ponieważ mogą utrzymywać / przycinać / dodawać drzewo do aplikacji. W przypadku S3 traktujesz taką strukturę jako rodzaj indeksu lub tagu wyszukiwania.

Aby manipulować obiektem w S3, potrzebujesz boto3.client lub boto3.resource, np. Aby wyświetlić wszystkie obiekty

import boto3 
s3 = boto3.client("s3")
all_objects = s3.list_objects(Bucket = 'bucket-name') 

http://boto3.readthedocs.org/en/latest/reference/services/s3.html#S3.Client.list_objects

W rzeczywistości, jeśli nazwa obiektu s3 jest przechowywana przy użyciu separatora „/”. Nowsza wersja list_objects (list_objects_v2) umożliwia ograniczenie odpowiedzi do kluczy rozpoczynających się od określonego prefiksu.

Aby ograniczyć elementy do elementów w określonych podfolderach:

    import boto3 
    s3 = boto3.client("s3")
    response = s3.list_objects_v2(
            Bucket=BUCKET,
            Prefix ='DIR1/DIR2',
            MaxKeys=100 )

Dokumentacja

Inną opcją jest użycie funkcji os.path w języku Python do wyodrębnienia prefiksu folderu. Problem polega na tym, że będzie to wymagało wypisywania obiektów z niepożądanych katalogów.

import os
s3_key = 'first-level/1456753904534/part-00014'
filename = os.path.basename(s3_key) 
foldername = os.path.dirname(s3_key)

# if you are not using conventional delimiter like '#' 
s3_key = 'first-level#1456753904534#part-00014
filename = s3_key.split("#")[-1]

Przypomnienie o boto3: boto3.resource to fajny interfejs API wysokiego poziomu. Korzystanie z boto3.client w porównaniu z boto3.resource ma swoje zalety i wady. Jeśli tworzysz wewnętrzną bibliotekę współdzieloną, użycie boto3.resource zapewni warstwę czarnej skrzynki nad używanymi zasobami.


1
Daje mi to taki sam wynik, jaki otrzymuję, próbując odpowiedzieć na pytanie. Myślę, że będę musiał rozwiązać trudny sposób, chwytając wszystkie klucze z zwróconych obiektów i dzieląc ciąg, aby uzyskać nazwę folderu.
mar tin

1
@martina: leniwy Python rozdziela i zbiera ostatnie dane z listy, np. nazwa_pliku = nazwa_kluczy.split ("/") [- 1]
mootmoot

1
@martin directory_name = os.path.dirname(directory/path/and/filename.txt)ifile_name = os.path.basename(directory/path/and/filename.txt)
jkdev

106

Poniższy fragment kodu zwraca TYLKO „podfoldery” w „folderze” z zasobnika s3.

import boto3
bucket = 'my-bucket'
#Make sure you provide / in the end
prefix = 'prefix-name-with-slash/'  

client = boto3.client('s3')
result = client.list_objects(Bucket=bucket, Prefix=prefix, Delimiter='/')
for o in result.get('CommonPrefixes'):
    print 'sub folder : ', o.get('Prefix')

Więcej informacji można znaleźć na stronie https://github.com/boto/boto3/issues/134


12
A jeśli chcę wyświetlić zawartość określonego podfolderu?
azhar22k

1
@ azhar22k, zakładam, że możesz po prostu uruchomić tę funkcję rekurencyjnie dla każdego „podfolderu”.
Serban Cezar

A jeśli istnieje ponad 1000 różnych prefiksów?
Kostrahb

38

Zajęło mi to dużo czasu, aby się zorientować, ale w końcu oto prosty sposób na wyświetlenie zawartości podfolderu w zasobniku S3 przy użyciu boto3. Mam nadzieję, że to pomoże

prefix = "folderone/foldertwo/"
s3 = boto3.resource('s3')
bucket = s3.Bucket(name="bucket_name_here")
FilesNotFound = True
for obj in bucket.objects.filter(Prefix=prefix):
     print('{0}:{1}'.format(bucket.name, obj.key))
     FilesNotFound = False
if FilesNotFound:
     print("ALERT", "No file in {0}/{1}".format(bucket, prefix))

3
co jeśli twój folder zawiera ogromną liczbę obiektów?
Pierre D

3
Chodzi mi o to, że jest to okropnie nieefektywne rozwiązanie. S3 jest zbudowany tak, aby radzić sobie z dowolnymi separatorami w kluczach. Na przykład '/'. Dzięki temu możesz pomijać „foldery” pełne obiektów bez konieczności ich stronicowania. A potem, nawet jeśli nalegasz na pełną listę (tj. „Rekurencyjny” odpowiednik w aws cli), to musisz użyć stronicowania, albo wymienisz tylko pierwsze 1000 obiektów.
Pierre D

To świetna odpowiedź. Dla tych, którzy tego potrzebują, zastosowałem limitdo niego w mojej pochodnej odpowiedzi .
Acumenus

38

Krótka odpowiedź :

  • Użyj Delimiter='/'. Pozwala to uniknąć rekurencyjnego wyświetlania twojego zasobnika. Niektóre odpowiedzi błędnie sugerują zrobienie pełnej listy i użycie pewnych operacji na łańcuchach znaków w celu pobrania nazw katalogów. To mogłoby być okropnie nieefektywne. Pamiętaj, że S3 praktycznie nie ma ograniczenia liczby obiektów, które może zawierać zasobnik. Wyobraź sobie więc, że między bar/a foo/masz bilion obiektów: czekałeś bardzo długo, aby je zdobyć ['bar/', 'foo/'].

  • Użyj Paginators. Z tego samego powodu (S3 jest przybliżeniem nieskończoności przez inżyniera), musisz wyświetlić listę stron i unikać przechowywania całej listy w pamięci. Zamiast tego potraktuj swój „lister” jako iterator i obsługuj generowany przez niego strumień.

  • Użyj boto3.client, nie boto3.resource. Wydaje się, że resourcewersja nie radzi sobie dobrze z tą Delimiteropcją. Jeśli masz zasób, powiedzmy bucket = boto3.resource('s3').Bucket(name), można uzyskać odpowiedniego klienta z: bucket.meta.client.

Długa odpowiedź :

Poniżej znajduje się iterator, którego używam do prostych zasobników (bez obsługi wersji).

import boto3
from collections import namedtuple
from operator import attrgetter


S3Obj = namedtuple('S3Obj', ['key', 'mtime', 'size', 'ETag'])


def s3list(bucket, path, start=None, end=None, recursive=True, list_dirs=True,
           list_objs=True, limit=None):
    """
    Iterator that lists a bucket's objects under path, (optionally) starting with
    start and ending before end.

    If recursive is False, then list only the "depth=0" items (dirs and objects).

    If recursive is True, then list recursively all objects (no dirs).

    Args:
        bucket:
            a boto3.resource('s3').Bucket().
        path:
            a directory in the bucket.
        start:
            optional: start key, inclusive (may be a relative path under path, or
            absolute in the bucket)
        end:
            optional: stop key, exclusive (may be a relative path under path, or
            absolute in the bucket)
        recursive:
            optional, default True. If True, lists only objects. If False, lists
            only depth 0 "directories" and objects.
        list_dirs:
            optional, default True. Has no effect in recursive listing. On
            non-recursive listing, if False, then directories are omitted.
        list_objs:
            optional, default True. If False, then directories are omitted.
        limit:
            optional. If specified, then lists at most this many items.

    Returns:
        an iterator of S3Obj.

    Examples:
        # set up
        >>> s3 = boto3.resource('s3')
        ... bucket = s3.Bucket(name)

        # iterate through all S3 objects under some dir
        >>> for p in s3ls(bucket, 'some/dir'):
        ...     print(p)

        # iterate through up to 20 S3 objects under some dir, starting with foo_0010
        >>> for p in s3ls(bucket, 'some/dir', limit=20, start='foo_0010'):
        ...     print(p)

        # non-recursive listing under some dir:
        >>> for p in s3ls(bucket, 'some/dir', recursive=False):
        ...     print(p)

        # non-recursive listing under some dir, listing only dirs:
        >>> for p in s3ls(bucket, 'some/dir', recursive=False, list_objs=False):
        ...     print(p)
"""
    kwargs = dict()
    if start is not None:
        if not start.startswith(path):
            start = os.path.join(path, start)
        # note: need to use a string just smaller than start, because
        # the list_object API specifies that start is excluded (the first
        # result is *after* start).
        kwargs.update(Marker=__prev_str(start))
    if end is not None:
        if not end.startswith(path):
            end = os.path.join(path, end)
    if not recursive:
        kwargs.update(Delimiter='/')
        if not path.endswith('/'):
            path += '/'
    kwargs.update(Prefix=path)
    if limit is not None:
        kwargs.update(PaginationConfig={'MaxItems': limit})

    paginator = bucket.meta.client.get_paginator('list_objects')
    for resp in paginator.paginate(Bucket=bucket.name, **kwargs):
        q = []
        if 'CommonPrefixes' in resp and list_dirs:
            q = [S3Obj(f['Prefix'], None, None, None) for f in resp['CommonPrefixes']]
        if 'Contents' in resp and list_objs:
            q += [S3Obj(f['Key'], f['LastModified'], f['Size'], f['ETag']) for f in resp['Contents']]
        # note: even with sorted lists, it is faster to sort(a+b)
        # than heapq.merge(a, b) at least up to 10K elements in each list
        q = sorted(q, key=attrgetter('key'))
        if limit is not None:
            q = q[:limit]
            limit -= len(q)
        for p in q:
            if end is not None and p.key >= end:
                return
            yield p


def __prev_str(s):
    if len(s) == 0:
        return s
    s, c = s[:-1], ord(s[-1])
    if c > 0:
        s += chr(c - 1)
    s += ''.join(['\u7FFF' for _ in range(10)])
    return s

Test :

Poniższe informacje są pomocne w testowaniu zachowania paginatori list_objects. Tworzy szereg katalogów i plików. Ponieważ strony zawierają do 1000 wpisów, używamy wielokrotności tej dla katalogów i plików. dirszawiera tylko katalogi (każdy ma jeden obiekt). mixedzawiera mieszankę katalogów i obiektów, ze stosunkiem 2 obiektów dla każdego katalogu (plus oczywiście jeden obiekt w katalogu; S3 przechowuje tylko obiekty).

import concurrent
def genkeys(top='tmp/test', n=2000):
    for k in range(n):
        if k % 100 == 0:
            print(k)
        for name in [
            os.path.join(top, 'dirs', f'{k:04d}_dir', 'foo'),
            os.path.join(top, 'mixed', f'{k:04d}_dir', 'foo'),
            os.path.join(top, 'mixed', f'{k:04d}_foo_a'),
            os.path.join(top, 'mixed', f'{k:04d}_foo_b'),
        ]:
            yield name


with concurrent.futures.ThreadPoolExecutor(max_workers=32) as executor:
    executor.map(lambda name: bucket.put_object(Key=name, Body='hi\n'.encode()), genkeys())

Wynikowa struktura to:

./dirs/0000_dir/foo
./dirs/0001_dir/foo
./dirs/0002_dir/foo
...
./dirs/1999_dir/foo
./mixed/0000_dir/foo
./mixed/0000_foo_a
./mixed/0000_foo_b
./mixed/0001_dir/foo
./mixed/0001_foo_a
./mixed/0001_foo_b
./mixed/0002_dir/foo
./mixed/0002_foo_a
./mixed/0002_foo_b
...
./mixed/1999_dir/foo
./mixed/1999_foo_a
./mixed/1999_foo_b

Po odrobinie przeróbki kodu podanego powyżej, s3listaby sprawdzić odpowiedzi z paginator, możesz zauważyć kilka zabawnych faktów:

  • MarkerJest naprawdę wykluczają. Podane Marker=topdir + 'mixed/0500_foo_a'spowoduje, że lista rozpocznie się po tym kluczu (zgodnie z API AmazonS3 ), tj .../mixed/0500_foo_b. Od. To jest powód __prev_str().

  • Używając Delimiterpodczas listowania mixed/, każda odpowiedź z paginatorzawiera 666 kluczy i 334 typowe prefiksy. Całkiem dobrze nie tworzy ogromnych odpowiedzi.

  • Z kolei podczas wyświetlania listy dirs/każda odpowiedź z elementu paginatorzawiera 1000 wspólnych prefiksów (bez kluczy).

  • Przekazywanie limitu w postaci PaginationConfig={'MaxItems': limit}ogranicza tylko liczbę kluczy, a nie wspólne przedrostki. Zajmujemy się tym przez dalsze obcinanie strumienia naszego iteratora.


@Mehdi: to naprawdę niezbyt skomplikowane, jak na system, który oferuje tak niewiarygodną skalę i niezawodność. Jeśli kiedykolwiek masz do czynienia z więcej niż kilkuset TB, docenisz to, co oferują. Pamiętaj, że dyski zawsze mają MTBF> 0 ... Pomyśl o konsekwencjach dla przechowywania danych na dużą skalę. Zastrzeżenie: Jestem aktywnym i szczęśliwym użytkownikiem AWS, nie mam innego połączenia, z wyjątkiem tego, że pracowałem na danych w skali petabajtów od 2007 roku i było to znacznie trudniejsze.
Pierre D

16

Z S3 wynika, że ​​nie ma folderów / katalogów tylko klucze. Widoczna struktura folderów jest poprzedzany tylko do nazwy pliku , aby stać się „klucz”, tak aby wyświetlić zawartość myBucket„s some/path/to/the/file/można spróbować:

s3 = boto3.client('s3')
for obj in s3.list_objects_v2(Bucket="myBucket", Prefix="some/path/to/the/file/")['Contents']:
    print(obj['Key'])

co dałoby ci coś takiego:

some/path/to/the/file/yo.jpg
some/path/to/the/file/meAndYou.gif
...

To dobra odpowiedź, ale pozwoli pobrać tylko 1000 obiektów i nie więcej. Stworzyłem pochodną odpowiedź, która może pobrać większą liczbę obiektów.
Acumenus

tak, @Acumenus, myślę, że twoja odpowiedź jest bardziej złożona
CpILL

16

Miałem ten sam problem, ale udało mi się go rozwiązać za pomocą boto3.clienti list_objects_v2z parametrami Bucketi StartAfter.

s3client = boto3.client('s3')
bucket = 'my-bucket-name'
startAfter = 'firstlevelFolder/secondLevelFolder'

theobjects = s3client.list_objects_v2(Bucket=bucket, StartAfter=startAfter )
for object in theobjects['Contents']:
    print object['Key']

Wynik wyjściowy dla powyższego kodu będzie wyglądał następująco:

firstlevelFolder/secondLevelFolder/item1
firstlevelFolder/secondLevelFolder/item2

Boto3 list_objects_v2 Dokumentacja

Aby usunąć tylko nazwę katalogu dla secondLevelFolder, właśnie użyłem metody Pythona split():

s3client = boto3.client('s3')
bucket = 'my-bucket-name'
startAfter = 'firstlevelFolder/secondLevelFolder'

theobjects = s3client.list_objects_v2(Bucket=bucket, StartAfter=startAfter )
for object in theobjects['Contents']:
    direcoryName = object['Key'].encode("string_escape").split('/')
    print direcoryName[1]

Wynik wyjściowy dla powyższego kodu będzie wyglądał następująco:

secondLevelFolder
secondLevelFolder

Dokumentacja Python split ()

Jeśli chcesz uzyskać nazwę katalogu ORAZ nazwę elementu zawartości, zamień linię drukowania na następującą:

print "{}/{}".format(fileName[1], fileName[2])

Zostaną wyświetlone następujące informacje:

secondLevelFolder/item2
secondLevelFolder/item2

Mam nadzieję że to pomoże


8

U mnie działa ... Obiekty S3:

s3://bucket/
    form1/
       section11/
          file111
          file112
       section12/
          file121
    form2/
       section21/
          file211
          file112
       section22/
          file221
          file222
          ...
      ...
   ...

Za pomocą:

from boto3.session import Session
s3client = session.client('s3')
resp = s3client.list_objects(Bucket=bucket, Prefix='', Delimiter="/")
forms = [x['Prefix'] for x in resp['CommonPrefixes']] 

otrzymujemy:

form1/
form2/
...

Z:

resp = s3client.list_objects(Bucket=bucket, Prefix='form1/', Delimiter="/")
sections = [x['Prefix'] for x in resp['CommonPrefixes']] 

otrzymujemy:

form1/section11/
form1/section12/

6

Cli AWS robi to (prawdopodobnie bez pobierania i iterowania wszystkich kluczy w wiadrze) podczas uruchamiania aws s3 ls s3://my-bucket/, więc pomyślałem, że musi być sposób na użycie boto3.

https://github.com/aws/aws-cli/blob/0fedc4c1b6a7aee13e2ed10c3ada778c702c22c3/awscli/customizations/s3/subcommands.py#L499

Wygląda na to, że rzeczywiście używają Prefiksu i Ogranicznika - udało mi się napisać funkcję, która dostarczyłaby mi wszystkie katalogi na poziomie głównym wiadra, modyfikując nieco ten kod:

def list_folders_in_bucket(bucket):
    paginator = boto3.client('s3').get_paginator('list_objects')
    folders = []
    iterator = paginator.paginate(Bucket=bucket, Prefix='', Delimiter='/', PaginationConfig={'PageSize': None})
    for response_data in iterator:
        prefixes = response_data.get('CommonPrefixes', [])
        for prefix in prefixes:
            prefix_name = prefix['Prefix']
            if prefix_name.endswith('/'):
                folders.append(prefix_name.rstrip('/'))
    return folders

2

Oto możliwe rozwiązanie:

def download_list_s3_folder(my_bucket,my_folder):
    import boto3
    s3 = boto3.client('s3')
    response = s3.list_objects_v2(
        Bucket=my_bucket,
        Prefix=my_folder,
        MaxKeys=1000)
    return [item["Key"] for item in response['Contents']]

1

Za pomocą boto3.resource

Opiera się to na odpowiedzi itz-azhar dotyczącej zastosowania opcjonalnego limit. Jest oczywiście znacznie prostszy w użyciu niż boto3.clientwersja.

import logging
from typing import List, Optional

import boto3
from boto3_type_annotations.s3 import ObjectSummary  # pip install boto3_type_annotations

log = logging.getLogger(__name__)
_S3_RESOURCE = boto3.resource("s3")

def s3_list(bucket_name: str, prefix: str, *, limit: Optional[int] = None) -> List[ObjectSummary]:
    """Return a list of S3 object summaries."""
    # Ref: https://stackoverflow.com/a/57718002/
    return list(_S3_RESOURCE.Bucket(bucket_name).objects.limit(count=limit).filter(Prefix=prefix))


if __name__ == "__main__":
    s3_list("noaa-gefs-pds", "gefs.20190828/12/pgrb2a", limit=10_000)

Za pomocą boto3.client

Wykorzystuje list_objects_v2i rozwija odpowiedź CpILL, aby umożliwić pobranie ponad 1000 obiektów.

import logging
from typing import cast, List

import boto3

log = logging.getLogger(__name__)
_S3_CLIENT = boto3.client("s3")

def s3_list(bucket_name: str, prefix: str, *, limit: int = cast(int, float("inf"))) -> List[dict]:
    """Return a list of S3 object summaries."""
    # Ref: https://stackoverflow.com/a/57718002/
    contents: List[dict] = []
    continuation_token = None
    if limit <= 0:
        return contents
    while True:
        max_keys = min(1000, limit - len(contents))
        request_kwargs = {"Bucket": bucket_name, "Prefix": prefix, "MaxKeys": max_keys}
        if continuation_token:
            log.info(  # type: ignore
                "Listing %s objects in s3://%s/%s using continuation token ending with %s with %s objects listed thus far.",
                max_keys, bucket_name, prefix, continuation_token[-6:], len(contents))  # pylint: disable=unsubscriptable-object
            response = _S3_CLIENT.list_objects_v2(**request_kwargs, ContinuationToken=continuation_token)
        else:
            log.info("Listing %s objects in s3://%s/%s with %s objects listed thus far.", max_keys, bucket_name, prefix, len(contents))
            response = _S3_CLIENT.list_objects_v2(**request_kwargs)
        assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
        contents.extend(response["Contents"])
        is_truncated = response["IsTruncated"]
        if (not is_truncated) or (len(contents) >= limit):
            break
        continuation_token = response["NextContinuationToken"]
    assert len(contents) <= limit
    log.info("Returning %s objects from s3://%s/%s.", len(contents), bucket_name, prefix)
    return contents


if __name__ == "__main__":
    s3_list("noaa-gefs-pds", "gefs.20190828/12/pgrb2a", limit=10_000)

0

Po pierwsze, w S3 nie ma prawdziwej koncepcji folderów. Na pewno możesz mieć plik @ '/folder/subfolder/myfile.txt'bez folderu ani podfolderu.

Aby „zasymulować” folder w S3, należy utworzyć pusty plik z „/” na końcu jego nazwy (zobacz boto Amazon S3 - jak utworzyć folder? )

W przypadku swojego problemu prawdopodobnie powinieneś użyć metody get_all_keysz dwoma parametrami: prefixidelimiter

https://github.com/boto/boto/blob/develop/boto/s3/bucket.py#L427

for key in bucket.get_all_keys(prefix='first-level/', delimiter='/'):
    print(key.name)

1
Obawiam się, że nie mam metody get_all_keys na obiekcie wiadra. Używam boto3 w wersji 1.2.3.
Mar cyny

Właśnie sprawdziłem boto 1.2a: tam, bucket ma metodę listz prefixi delimiter. Myślę, że to powinno działać.
Pirheas

1
Obiekt Bucket pobrany, gdy publikuję w pytaniu, nie ma tych metod. Jestem na boto3 1.2.6, do jakiej wersji odnosi się twój link?
Mar cyny


0

Wiem, że omawiamy tutaj temat boto3, ale uważam, że zwykle szybsze i bardziej intuicyjne jest po prostu użycie awscli do czegoś takiego - awscli zachowuje więcej możliwości niż boto3 ze względu na swoją wartość.

Na przykład, jeśli mam obiekty zapisane w „podfolderach” powiązanych z danym zasobnikiem, mogę je wszystkie wymienić za pomocą czegoś takiego:

1) „mydata” = nazwa zasobnika

2) „f1 / f2 / f3” = „ścieżka” prowadząca do „plików” lub obiektów

3) „foo2.csv, barfar.segy, gar.tar” = wszystkie obiekty „wewnątrz” f3

Możemy więc wyobrazić sobie „ścieżkę bezwzględną” prowadzącą do tych obiektów: „moje dane / f1 / f2 / f3 / foo2.csv” ...

Używając poleceń awscli, możemy łatwo wyświetlić listę wszystkich obiektów wewnątrz danego „podfolderu” poprzez:

aws s3 ls s3: // moje dane / f1 / f2 / f3 / --recursive


0

Poniżej znajduje się fragment kodu, który może obsłużyć paginację, jeśli próbujesz pobrać dużą liczbę obiektów zasobnika S3:

def get_matching_s3_objects(bucket, prefix="", suffix=""):

    s3 = boto3.client("s3")
    paginator = s3.get_paginator("list_objects_v2")

    kwargs = {'Bucket': bucket}

    # We can pass the prefix directly to the S3 API.  If the user has passed
    # a tuple or list of prefixes, we go through them one by one.
    if isinstance(prefix, str):
        prefixes = (prefix, )
    else:
        prefixes = prefix

    for key_prefix in prefixes:
        kwargs["Prefix"] = key_prefix

        for page in paginator.paginate(**kwargs):
            try:
                contents = page["Contents"]
            except KeyError:
                return

            for obj in contents:
                key = obj["Key"]
                if key.endswith(suffix):
                    yield obj

0

Jeśli chodzi o Boto 1.13.3, okazuje się, że jest to takie proste (jeśli pominiesz wszystkie kwestie dotyczące stronicowania, które zostały omówione w innych odpowiedziach):

def get_sub_paths(bucket, prefix):
s3 = boto3.client('s3')
response = s3.list_objects_v2(
    Bucket=bucket,
    Prefix=prefix,
    MaxKeys=1000)
return [item["Prefix"] for item in response['CommonPrefixes']]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.