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.