S3 klasöründeki alt klasör adlarını boto3'ten alma


94

Boto3 kullanarak AWS S3 klasörüme erişebilirim:

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

Artık first-levelpaket, örneğin bir zaman damgası ile adlandırılmış birkaç alt klasör içeren klasörü içerir 1456753904534. Yaptığım başka bir iş için bu alt klasörlerin adını bilmem gerekiyor ve boto3'ün bunları bana geri getirip getiremeyeceğini merak ediyorum.

Ben de denedim:

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

Bu, 'İçindekiler' anahtarı bana ikinci düzey zaman damgası dizinleri yerine tüm üçüncü düzey dosyaları veren bir sözlük verir, aslında aşağıdaki gibi şeyleri içeren bir liste

{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 ':' depo sınıfı '}

part-00014Sadece dizinin adını almak istiyorum iken, bu durumda belirli dosyaların alındığını görebilirsiniz . Prensipte dizin adını tüm yollardan çıkarabilirim ama ikinci seviyeye ulaşmak için üçüncü seviyedeki her şeyi almak çirkin ve pahalı!

Ayrıca burada bildirilen bir şeyi denedim :

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

ancak klasörleri istediğim düzeyde alamıyorum.

Bunu çözmenin bir yolu var mı?


Yani söylüyorsun bu işi yok ki? Bunu çalıştırdığınızda ne olduğunu yayınlayabilir misiniz?
Jordon Phillips

1
@JordonPhillips Gönderdiğiniz bağlantının ilk satırlarını denedim, buraya yapıştırdım ve metin dosyalarını paketin ilk seviyesinde alıyorum ve klasör yok.
mar tin

@mar tin Bu sorunu çözdünüz mü? Her kova alt klasöründe ilk öğeye ihtiyacım olduğu yerde benzer bir ikilemle karşı karşıyayım.
Ted Taylor of Life

1
@TedTaylorofLife Evet, tüm nesneleri /alıp alt klasörleri almak için ayırmaktan başka bir yol yok
mar tin

1
@ mar tin Yaptığım tek yol çıktıyı alıp bir metin biçimine atıp "/" ile virgülle sınırlandırmak ve ardından ilk öğeyi kopyalayıp yapıştırmak. Ne baş belası.
Ted Taylor of Life

Yanıtlar:


65

S3 bir nesne deposudur, gerçek dizin yapısına sahip değildir. "/" Oldukça kozmetiktir. İnsanların bir dizin yapısına sahip olmak istemelerinin bir nedeni, çünkü uygulamaya bir ağaç ekleyebilir / kesebilir / ekleyebilirler. S3 için, bu tür bir yapıya dizin türü veya arama etiketi olarak davranırsınız.

S3'te nesneyi işlemek için boto3.client veya boto3.resource'a ihtiyacınız vardır, örneğin tüm nesneleri listelemek için

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

Aslında, s3 nesne adı '/' ayırıcısı kullanılarak saklanırsa. List_objects'in (list_objects_v2) daha yeni sürümü, yanıtı belirtilen önekle başlayan anahtarlarla sınırlamanıza izin verir.

Öğeleri belirli alt klasörler altındaki öğelerle sınırlamak için:

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

Dokümantasyon

Başka bir seçenek de klasör önekini çıkarmak için python os.path işlevini kullanmaktır. Sorun şu ki, bu istenmeyen dizinlerden nesnelerin listelenmesini gerektirecek.

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]

Boto3 ile ilgili bir hatırlatma: boto3.resource güzel bir üst düzey API'dir. Boto3.client ve boto3.resource kullanmanın artıları ve eksileri vardır. Dahili paylaşılan kitaplık geliştirirseniz, boto3.resource'u kullanmak size kullanılan kaynaklar üzerinde bir kara kutu katmanı sağlayacaktır.


2
Bu bana sorudaki denememle aldığım sonucu veriyor. Sanırım, döndürülen nesnelerden tüm anahtarları alarak ve klasör adını almak için dizeyi bölerek zor yoldan çözmem gerekecek.
mar tin

1
@martina: tembel bir python bölünmesi ve listedeki son verileri alması örn. filename = keyname.split ("/") [- 1]
mootmoot

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

109

Aşağıdaki kod parçası YALNIZCA s3 paketinden bir "klasör" içindeki "alt klasörleri" döndürür.

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

Daha fazla ayrıntı için https://github.com/boto/boto3/issues/134 adresine başvurabilirsiniz.


12
Belirli bir alt klasörün içeriğini listelemek istersem ne olur?
azhar22k

1
@ azhar22k, her 'alt klasör' için işlevi yinelemeli olarak çalıştırabileceğinizi varsayıyorum.
Serban Cezar

Ya 1000'den fazla farklı önek varsa?
Kostrahb

42

Kısa cevap :

  • Kullanım Delimiter='/' . Bu, paketinizin yinelemeli bir listesini yapmaktan kaçınır. Buradaki bazı yanıtlar, yanlış bir şekilde tam bir liste yapmayı ve dizin adlarını almak için bir dizi düzenleme kullanmayı önerir. Bu korkunç derecede verimsiz olabilir. S3'ün, bir paketin içerebileceği nesne sayısı konusunda neredeyse hiçbir sınırı olmadığını unutmayın. Öyleyse, bar/ve arasında foo/bir trilyon nesneye sahip olduğunuzu hayal edin : elde etmek için çok uzun bir süre beklersiniz ['bar/', 'foo/'].

  • Kullanım Paginators . Aynı nedenle (S3 sonsuz bir mühendisin tahmindir), sen gerekir tüm bellekte listeleme depolamak sayfaları ve kaçınmak yoluyla sıralar. Bunun yerine, "dinleyicinizi" bir yineleyici olarak düşünün ve ürettiği akışı işleyin.

  • Kullanım boto3.client , değil boto3.resource. resourceVersiyon iyi işlemek için görünmüyor Delimiterseçeneği. Bir kaynağınız varsa,bucket = boto3.resource('s3').Bucket(name) , sen ile ilgili müşteri alabilirsiniz: bucket.meta.client.

Uzun cevap :

Aşağıda, basit kovalar için kullandığım bir yineleyici gösterilmektedir (sürüm işleme yok).

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

Ölçek :

Aşağıdaki davranışını test etmek yararlıdır paginatorvelist_objects . Bir dizi dizin ve dosya oluşturur. Sayfalar 1000 girişe kadar olduğundan, dizinler ve dosyalar için bunun bir katını kullanırız. dirsyalnızca dizinleri içerir (her biri bir nesneye sahiptir) mixedher bir dizin için 2 nesne oranına sahip (ayrıca dir altında bir nesne; S3 yalnızca nesneleri depolar) bir dizi ve nesne karışımı içerir.

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

Ortaya çıkan yapı:

./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

Yukarıda verilen kodun biraz düzeltilmesiyle s3list yaparak, yanıtları incelemek içinpaginator , bazı eğlenceli gerçekleri gözlemleyebilirsiniz:

  • MarkerGerçekten özeldir. Verilen Marker=topdir + 'mixed/0500_foo_a', listelemenin bu anahtardan sonra ( AmazonS3 API'ye göre ), yani.../mixed/0500_foo_b . Nedeni bu__prev_str() .

  • DelimiterListeleme sırasında kullanmamixed/ , gelen her yanıt paginator666 tuşları ve 334 ortak önekleri içerir. Çok büyük tepkiler oluşturmamakta oldukça iyidir.

  • Buna karşılık, listeleme sırasında dirs/ , her yanıt paginator1000 ortak ön ek içerir (ve anahtar içermez).

  • Biçiminde bir sınır geçmek PaginationConfig={'MaxItems': limit}, ortak önekleri değil, yalnızca anahtar sayısını sınırlar. Yineleyicimizin akışını daha da keserek bunun üstesinden geliyoruz.


@Mehdi: Bu kadar inanılmaz ölçek ve güvenilirlik sunan bir sistem için gerçekten çok karmaşık değil. Birkaç yüz TB'den daha fazlasıyla başa çıkarsanız, sundukları için bir takdir göreceksiniz. Unutmayın, sürücülerde her zaman MTBF> 0 ... Büyük ölçekli veri depolamanın sonuçlarını düşünün. Sorumluluk reddi: Aktif ve mutlu bir AWS kullanıcısıyım, başka bir bağlantı yok, ancak 2007'den beri petabayt ölçekli veriler üzerinde çalıştım ve eskiden çok daha zordu.
Pierre D

39

Bunu anlamam çok zaman aldı, ama nihayet işte boto3 kullanarak S3 kovasındaki bir alt klasörün içeriğini listelemenin basit bir yolu. Umarım yardımcı olur

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
ya klasörünüz çok sayıda nesne içeriyorsa?
Pierre D

3
Demek istediğim, bunun korkunç derecede verimsiz bir çözüm olduğu. S3, tuşlarda rastgele ayırıcılarla başa çıkmak için tasarlanmıştır. Örneğin '/',. Böylece, nesnelerle dolu "klasörleri", üzerlerine sayfalandırmak zorunda kalmadan atlayabilirsiniz. Ve sonra, tam bir listelemede ısrar etseniz bile (yani, aws cli'deki 'özyinelemeli' eşdeğeri), o zaman sayfalayıcıları kullanmalısınız, yoksa sadece ilk 1000 nesneyi listeleyeceksiniz.
Pierre D

Bu harika bir cevap. İhtiyaç duyanlar için, türetilmiş cevabımdalimit ona bir uyguladım .
Acumenus

16

S3 ile ilgili büyük fark, klasörler / dizinler sadece anahtarların olmadığıdır. Belirgin klasör yapısı sadece dosya adı önüne edilir böylece içeriğini listelemek için, 'Anahtar' olmayı myBuckets' some/path/to/the/file/deneyebilirsin:

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

bu size şöyle bir şey verir:

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

Bu iyi bir cevap, ancak yalnızca 1000 nesneye kadar geri getirecek ve daha fazlasını almayacak. Daha fazla sayıda nesneyi alabilen türetilmiş bir cevap ürettim.
Acumenus

evet, @Acumenus sanırım cevabınız daha karmaşık
CpILL

16

Ben aynı sorunu vardı ama kullanarak çözmek başardı boto3.clientve list_objects_v2ile Bucketve StartAfterparametreleri.

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

Yukarıdaki kod için çıktı sonucu aşağıdakileri gösterecektir:

firstlevelFolder/secondLevelFolder/item1
firstlevelFolder/secondLevelFolder/item2

Boto3 list_objects_v2 Belgeleri

Sadece dizin adını çıkarmak için secondLevelFolderpython yöntemini kullandım 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]

Yukarıdaki kod için çıktı sonucu aşağıdakileri gösterecektir:

secondLevelFolder
secondLevelFolder

Python split () Belgeler

Dizin adını VE içerik öğesi adını almak istiyorsanız, yazdırma satırını aşağıdakilerle değiştirin:

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

Ve aşağıdaki çıktı alınacaktır:

secondLevelFolder/item2
secondLevelFolder/item2

Bu yardımcı olur umarım


9

Aşağıdakiler benim için çalışıyor ... S3 nesneleri:

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

Kullanarak:

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

biz alırız:

form1/
form2/
...

İle:

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

biz alırız:

form1/section11/
form1/section12/

Bu benim için işe yarayan tek çözümdü çünkü paketin kök dizininde "klasörler" e ihtiyacım vardı, önek "" "olmalı, aksi halde" / "ile bitmeli
Oliver

7

AWS cli, koştuğunuzda bunu (muhtemelen kovadaki tüm tuşları getirmeden ve yinelemeden) yapar aws s3 ls s3://my-bucket/, bu yüzden boto3 kullanmanın bir yolu olması gerektiğini düşündüm.

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

Görünüşe göre Önek ve Sınırlayıcı kullanıyorlar - bu kodu biraz değiştirerek bana bir paketin kök düzeyinde tüm dizinleri getirecek bir işlev yazabildim:

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

İşte olası bir çözüm:

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

2

Neden s3pathonu çalışmak kadar kolay hale getiren paketi kullanmıyorsunuz pathlib? Ancak kullanmanız gerekiyorsa boto3:

Kullanma boto3.resource

Bu, bir isteğe bağlı uygulamak için itz-azhar tarafından verilen cevaba dayanır limit. Açıkçası, boto3.clientversiyondan çok daha basittir .

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)

Kullanma boto3.client

Bu, 1000'den fazla nesnenin alınmasına izin vermek için CpILL tarafındanlist_objects_v2 verilen yanıtı kullanır ve temel alır .

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

Her şeyden önce, S3'te gerçek bir klasör konsepti yoktur. Kesinlikle bir dosyaya sahip olabilirsiniz @'/folder/subfolder/myfile.txt' ve klasör veya alt klasörünüz olamaz.

S3'te bir klasörü "simüle etmek" için, adının sonunda "/" bulunan boş bir dosya oluşturmanız gerekir ( bkz.Amazon S3 boto - klasör nasıl oluşturulur? )

Sorununuz için, muhtemelen yöntemi get_all_keys2 parametreli kullanmalısınız: prefixvedelimiter

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
Korkarım kova nesnesinde get_all_keys yöntemine sahip değilim. Boto3 1.2.3 sürümünü kullanıyorum.
mar tin

Az önce boto 1.2a'ya baktım: orada, kova'nın ve listile bir yöntemi var . Sanırım işe yaramalı. prefixdelimiter
Pirheas

1
Ben soruda gönderirken alınan Bucket nesnesi bu yöntemlere sahip değil. Boto3 1.2.6'dayım, bağlantınız hangi sürüme atıfta bulunuyor?
mar tin


0

Burada tartışılan konunun boto3 olduğunu biliyorum, ancak awscli'yi kullanmanın genellikle daha hızlı ve sezgisel olduğunu görüyorum. böyle bir şey - awscli, boto3'ten daha fazla yeteneğe sahip.

Örneğin, belirli bir paketle ilişkili "alt klasörler" içinde kaydedilmiş nesnelerim varsa, bunların tümünü aşağıdaki gibi bir şeyle listeleyebilirim:

1) 'verilerim' = paket adı

2) 'f1 / f2 / f3' = "dosyalar" veya nesnelere giden "yol"

3) 'foo2.csv, barfar.segy, gar.tar' = "f3 içindeki" tüm nesneler

Dolayısıyla, bu nesnelere giden "mutlak yolu" düşünebiliriz: 'mydata / f1 / f2 / f3 / foo2.csv' ...

Awscli komutlarını kullanarak, belirli bir "alt klasördeki" tüm nesneleri şu şekilde kolayca listeleyebiliriz:

aws s3 ls s3: // verilerim / f1 / f2 / f3 / - yinelemeli


0

Aşağıda, çok sayıda S3 klasör nesnesini getirmeye çalışıyorsanız sayfalandırmayı işleyebilecek kod parçası verilmiştir:

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

ya ilk sayfa "CommonPrefixes" ile doluysa ve herhangi bir "İçerik" anahtarı sağlamıyorsa? Bence doğru uygulama eksik İçerik anahtarını atlayacak ve bir sonraki sayfadan devam edecektir.
Jan Vlcinsky

0

Boto 1.13.3'e gelince, bu kadar basit hale geliyor (diğer cevaplarda anlatılan tüm sayfalandırma hususlarını atlarsanız):

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']]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.