boto3 kullanarak s3'te bir kovada bir anahtar olup olmadığını kontrol edin


166

Boto3'te bir anahtar olup olmadığını bilmek istiyorum. Kova içeriğini değiştirebilir ve eşleşirse anahtarı kontrol edebilirim.

Ama bu daha uzun ve aşırı bir şey gibi görünüyor. Boto3 resmi belgeleri bunun nasıl yapılacağını açıkça belirtmektedir.

Belki de bariz olanı özlüyorum. Biri bana bunu nasıl başarabileceğimi gösterebilir mi?

Yanıtlar:


197

Boto 2'nin boto.s3.key.Keynesnesi exists, bir HEAD isteği yaparak ve sonuca bakarak anahtarın S3'te var olup olmadığını kontrol eden bir yönteme sahipti, ancak artık var olmadığı anlaşılıyor. Bunu kendiniz yapmak zorundasınız:

import boto3
import botocore

s3 = boto3.resource('s3')

try:
    s3.Object('my-bucket', 'dootdoot.jpg').load()
except botocore.exceptions.ClientError as e:
    if e.response['Error']['Code'] == "404":
        # The object does not exist.
        ...
    else:
        # Something else has gone wrong.
        raise
else:
    # The object does exist.
    ...

load() söz konusu nesne büyük olsa veya grubunuzda çok sayıda nesne olsa bile, tek bir anahtar için HEAD isteği yapar.

Elbette, nesnenin var olup olmadığını kontrol ediyor olabilirsiniz, çünkü onu kullanmayı planlıyorsunuz. Bu durumda, sadece unutabiliriz load()ve yapmak get()ya da download_file()doğrudan, daha sonra orada hata olaya el.


Hızlı cevap için teşekkürler Wander. Sadece boto3 için buna ihtiyacım var.
Prabhakar Shanmugam

12
Çünkü boto3, şu anda yapabileceğiniz en iyi şey head_object, anahtarın meta verilerini almak ve almak için aramak , sonra yoksa ortaya çıkan hatayı işlemek.
Wander Nauta

1
@Leonid Kesinlikle yapabilirsiniz, ancak bunu sadece size bağlı bir işlev veya yönteme sardıysanız. Örnek kodu biraz değiştirdim, böylece existsboolean gitti ve insanların bunu durumlarına uyarlamaları daha açık (umarım!).
Nauta

2
1; benim için çalışmıyor. Boto3 1.5.26 sürümünde e.response['Error']['Code']böyle bir değere sahip olduğunu görüyorum "NoSuchKey", değil "404". Bu yanıt yazıldığından beri bunun kütüphane sürümlerindeki bir fark veya API'nın kendisindeki bir değişiklik nedeniyle olup olmadığını kontrol etmedim. Her iki durumda da, boto3 versiyonumda, kontrol etmekten daha kısa bir yaklaşım e.response['Error']['Code']sadece s3.meta.client.exceptions.NoSuchKeyilk etapta yakalamaktır .
Mark Amery

2
bir s3 kullanıyorsanız client(aksine resource) a s3.head_object(Bucket='my_bucket', Key='my_key')yerines3.Object(...).load()
user2426679

128

Kontrol akışı için istisnalar kullanma konusunda büyük bir hayran değilim. Bu, boto3'te çalışan alternatif bir yaklaşımdır:

import boto3

s3 = boto3.resource('s3')
bucket = s3.Bucket('my-bucket')
key = 'dootdoot.jpg'
objs = list(bucket.objects.filter(Prefix=key))
if any([w.key == path_s3 for w in objs]):
    print("Exists!")
else:
    print("Doesn't exist")

Güncelleme için teşekkürler EvilPuppetMaster. Ne yazık ki en son kontrol ettiğimde liste grubu erişim haklarına sahip değildim. Cevabım sorum için uygun, bu yüzden sana oy verdim. Ama ilk cevabı çoktan cevap olarak işaretlemiştim. Yardımınız için teşekkürler.
Prabhakar Shanmugam

27
Bu bir listeleme isteği olarak sayılmaz (12,5 kat daha pahalı)? Bunu 100 milyon nesne için yaparsanız, bu biraz pahalı olabilir ... Yakalama-istisna yöntem maalesef en iyisi olduğunu hissediyorum.
Pierre D

21
Liste, istek başına 12,5 kat daha pahalı olabilir, ancak tek bir istek, tek bir alma işleminin yalnızca bir tane döndürebileceği 100 milyon nesne döndürebilir. Varsayımsal durumunuzda, 100 milyonun tamamını listeyle birlikte almak ve daha sonra yerel olarak karşılaştırmak, 100m bireysel kazanmaya göre daha ucuz olacaktır. Her nesne için http gidiş dönüşüne ihtiyacınız olmayacağından 1000 kat daha hızlı bahsetmiyoruz.
EvilPuppetMaster


2
@ user3186866 S3'te gerçekten "klasörler" olmadığı için. Tüm nesneler verilen yollarında dosya olarak bulunur. Klasörler, depolama alanımızın yapısını düzenlememize ve anlamamıza yardımcı olan bir araçtır, ancak gerçekte S3 kovaları sadece kovalardır.
ibtokin

115

Bulduğum en kolay yol (ve muhtemelen en verimli yöntem):

import boto3
from botocore.errorfactory import ClientError

s3 = boto3.client('s3')
try:
    s3.head_object(Bucket='bucket_name', Key='file_path')
except ClientError:
    # Not found
    pass

2
Not: Bir rol kullanıyorsanız veya anahtarlarınızda .aws config varsa aws_access_key_id / aws_secret_access_key vb. İletmeniz gerekmez. Yapmanız gereken tek şey şu olabilirs3 = boto3.client('s3')
Andy Hayden

20
Bu testin eklenmesi, istisnayı yükselten başka bir hata yerine nesnenin gerçekten var olmadığından biraz daha fazla güven verdiğini düşünüyorum - 'e' nin ClientError istisna örneği olduğunu unutmayın:if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
Richard

@AndyHayden Her deneme, aws maliyeti açısından neyi sayacaktı?
döngü

2
@Taylor bu bir get isteği ancak veri aktarımı olmadan.
Andy Hayden

1
ClientError sadece 404 için değil, 400 için bir yakalamadır, bu nedenle sağlam değildir.
mickzer

21

Boto3'te, bir klasör (önek) veya list_objects kullanan bir dosyayı kontrol ediyorsanız. Yanıt dikmesinde 'İçindekiler'in varlığını nesnenin var olup olmadığını kontrol etmek için kullanabilirsiniz. @EvilPuppetMaster'ın önerdiği gibi yakalama / yakalamadan kaçınmanın başka bir yolu

import boto3
client = boto3.client('s3')
results = client.list_objects(Bucket='my-bucket', Prefix='dootdoot.jpg')
return 'Contents' in results

2
Bunda bir sorun vardı. list_objects ("2000"), "2000-01", "2000-02" gibi anahtarları döndürür
Gunnar Cheng


Bu, s3:GetObjectyalnızca s3:ListBucketizinleri gerektirmediği için en etkili çözümdür
Vishrant

11

Sadece değil, clientaynı bucketzamanda:

import boto3
import botocore
bucket = boto3.resource('s3', region_name='eu-west-1').Bucket('my-bucket')

try:
  bucket.Object('my-file').get()
except botocore.exceptions.ClientError as ex:
  if ex.response['Error']['Code'] == 'NoSuchKey':
    print('NoSuchKey')

3
Nesneyi almak istemeyebilirsiniz, ama sadece orada olup olmadığına bakın. Buradaki nesneyi diğer örneklerde olduğu gibi yönlendiren bir yöntem kullanabilirsiniz bucket.Object(key).last_modified.
ryanjdillon

10

Temelde boto3'ün etrafındaki tipik dosya sistemi stili işlemlerini gösteren bir sarıcı olan S3F'leri kullanabilirsiniz :

import s3fs
s3 = s3fs.S3FileSystem()
s3.exists('myfile.txt')

Bunun işe yarayacağını düşünmeme rağmen, soru boto3 ile nasıl yapılacağını soruyor; bu durumda, ek bir kütüphane kurmadan sorunu çözmek pratiktir.
paulkernfeld

6

Sadece bir anahtarın var olup olmadığını kontrol etmek istediğinizi varsayarsak (sessizce üzerine yazmak yerine), önce bu kontrolü yapın:

import boto3

def key_exists(mykey, mybucket):
  s3_client = boto3.client('s3')
  response = s3_client.list_objects_v2(Bucket=mybucket, Prefix=mykey)
  if response:
      for obj in response['Contents']:
          if mykey == obj['Key']:
              return True
  return False

if key_exists('someprefix/myfile-abc123', 'my-bucket-name'):
    print("key exists")
else:
    print("safe to put new bucket object")
    # try:
    #     resp = s3_client.put_object(Body="Your string or file-like object",
    #                                 Bucket=mybucket,Key=mykey)
    # ...check resp success and ClientError exception for errors...

5
import boto3
client = boto3.client('s3')
s3_key = 'Your file without bucket name e.g. abc/bcd.txt'
bucket = 'your bucket name'
content = client.head_object(Bucket=bucket,Key=s3_key)
    if content.get('ResponseMetadata',None) is not None:
        print "File exists - s3://%s/%s " %(bucket,s3_key) 
    else:
        print "File does not exist - s3://%s/%s " %(bucket,s3_key)

5

FWIW, işte kullandığım çok basit fonksiyonlar

import boto3

def get_resource(config: dict={}):
    """Loads the s3 resource.

    Expects AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to be in the environment
    or in a config dictionary.
    Looks in the environment first."""

    s3 = boto3.resource('s3',
                        aws_access_key_id=os.environ.get(
                            "AWS_ACCESS_KEY_ID", config.get("AWS_ACCESS_KEY_ID")),
                        aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY", config.get("AWS_SECRET_ACCESS_KEY")))
    return s3


def get_bucket(s3, s3_uri: str):
    """Get the bucket from the resource.
    A thin wrapper, use with caution.

    Example usage:

    >> bucket = get_bucket(get_resource(), s3_uri_prod)"""
    return s3.Bucket(s3_uri)


def isfile_s3(bucket, key: str) -> bool:
    """Returns T/F whether the file exists."""
    objs = list(bucket.objects.filter(Prefix=key))
    return len(objs) == 1 and objs[0].key == key


def isdir_s3(bucket, key: str) -> bool:
    """Returns T/F whether the directory exists."""
    objs = list(bucket.objects.filter(Prefix=key))
    return len(objs) > 1

1
Bu bir 'dosya' ile karşılaştırıldığında bir 'klasör' için varlığını kontrol ele gördüm tek yanıt budur. bir klasördeki belirli dosyaları değil, belirli bir klasörün var olup olmadığını bilmesi gereken yordamlar için çok önemlidir.
dave campbell

Bu dikkatli bir yanıt olmakla birlikte, kullanıcı bu durumda bir klasör kavramının yanıltıcı olduğunu anlarsa yararlıdır. Boş bir 'klasör' S3 içinde bir kova içinde olabilir ve eğer öyleyse isdir_s3 dönecekse False, ifadeyi> 0 olarak değiştirilmiş gibi cevabı düzenlemeyi düşündüğümü sıralamak için birkaç dakikamı aldı. beklediğiniz sonuç
PyNEwbie

4

Bu hem öneki hem de anahtarı kontrol edebilir ve en fazla 1 anahtar getirir.

def prefix_exits(bucket, prefix):
    s3_client = boto3.client('s3')
    res = s3_client.list_objects_v2(Bucket=bucket, Prefix=prefix, MaxKeys=1)
    return 'Contents' in res

3

Bu basit deneyin

import boto3
s3 = boto3.resource('s3')
bucket = s3.Bucket('mybucket_name') # just Bucket name
file_name = 'A/B/filename.txt'      # full file path
obj = list(bucket.objects.filter(Prefix=file_name))
if len(obj) > 0:
    print("Exists")
else:
    print("Not Exists")


1
S3_REGION="eu-central-1"
bucket="mybucket1"
name="objectname"

import boto3
from botocore.client import Config
client = boto3.client('s3',region_name=S3_REGION,config=Config(signature_version='s3v4'))
list = client.list_objects_v2(Bucket=bucket,Prefix=name)
for obj in list.get('Contents', []):
    if obj['Key'] == name: return True
return False

1

Boto3 için ObjectSummary bir amacı olup olmadığını kontrol etmek için kullanılabilir.

Amazon S3 demetinde depolanan bir nesnenin özetini içerir. Bu nesne, nesnenin tam meta verilerini veya herhangi bir içeriğini içermiyor

import boto3
from botocore.errorfactory import ClientError
def path_exists(path, bucket_name):
    """Check to see if an object exists on S3"""
    s3 = boto3.resource('s3')
    try:
        s3.ObjectSummary(bucket_name=bucket_name, key=path).load()
    except ClientError as e:
        if e.response['Error']['Code'] == "404":
            return False
        else:
            raise e
    return True

path_exists('path/to/file.html')

In ObjectSummary.load

ObjectSummary kaynağının özniteliklerini güncelleştirmek için s3.Client.head_object öğesini çağırır.

Bu, kullanmamayı planlıyorsanız ObjectSummaryyerine kullanabileceğinizi gösterir . Fonksiyon sadece özetini alır nesneyi almak değil.Objectget()load()


1

İşte benim için çalışan bir çözüm. Bir uyarı, anahtarın tam biçimini önceden bilmemdir, bu yüzden sadece tek bir dosyayı listeliyorum

import boto3

# The s3 base class to interact with S3
class S3(object):
  def __init__(self):
    self.s3_client = boto3.client('s3')

  def check_if_object_exists(self, s3_bucket, s3_key):
    response = self.s3_client.list_objects(
      Bucket = s3_bucket,
      Prefix = s3_key
      )
    if 'ETag' in str(response):
      return True
    else:
      return False

if __name__ == '__main__':
  s3  = S3()
  if s3.check_if_object_exists(bucket, key):
    print "Found S3 object."
  else:
    print "No object found."

1

bunun için Boto3'ü kullanabilirsiniz.

import boto3
s3 = boto3.resource('s3')
bucket = s3.Bucket('my-bucket')
objs = list(bucket.objects.filter(Prefix=key))
if(len(objs)>0):
    print("key exists!!")
else:
    print("key doesn't exist!")

Burada anahtar var olup olmadığını kontrol etmek istediğiniz yoldur


Basit bir %timeittestten bu en hızlı seçenek gibi görünüyor
Itamar Katz

1

get()Yöntem ile gerçekten çok basit

import botocore
from boto3.session import Session
session = Session(aws_access_key_id='AWS_ACCESS_KEY',
                aws_secret_access_key='AWS_SECRET_ACCESS_KEY')
s3 = session.resource('s3')
bucket_s3 = s3.Bucket('bucket_name')

def not_exist(file_key):
    try:
        file_details = bucket_s3.Object(file_key).get()
        # print(file_details) # This line prints the file details
        return False
    except botocore.exceptions.ClientError as e:
        if e.response['Error']['Code'] == "NoSuchKey": # or you can check with e.reponse['HTTPStatusCode'] == '404'
            return True
        return False # For any other error it's hard to determine whether it exists or not. so based on the requirement feel free to change it to True/ False / raise Exception

print(not_exist('hello_world.txt')) 

Sağlam değil, birçok nedenden dolayı istisna atılabilir, örneğin HTTP 500 ve bu kod 404 kabul eder.
mickzer

Ancak dosyanın erişilebilir olup olmadığı hakkında bilgiye ihtiyacımız var. Var ve erişilemez, o zaman var olmaya eşdeğerdir. sağ?
isambitd

@mickzer değişiklikleri şimdi kontrol edin.
isambitd

1
Size bir önceki yorumu yanıtlamak için, Hayır, davranışı, bir HTTP 500 üzerinde yeniden denemek, 401/403 doğrulamak için olabilir vb. Gerçek hata kodunu kontrol etmek önemlidir.
mickzer

0

S3 kovasında dosyanın var olup olmadığını kontrol etmenin basit bir yolu vardır. Bunun için istisna kullanmamız gerekmiyor

sesssion = boto3.Session(aws_access_key_id, aws_secret_access_key)
s3 = session.client('s3')

object_name = 'filename'
bucket = 'bucketname'
obj_status = s3.list_objects(Bucket = bucket, Prefix = object_name)
if obj_status.get('Contents'):
    print("File exists")
else:
    print("File does not exists")

Grupta ile başlayan bir dosya grupta varsa bu yanlış olur object_name. Örneğin my_file.txt.oldversion, kontrol ederseniz yanlış pozitif dönecektir my_file.txt. Çoğu için biraz kenar durumu, ancak "dosya var" gibi geniş bir şey için, muhtemelen uygulamanız boyunca muhtemelen dikkate almaya değer.
Andrew Schwartz

0

Bir dizine eşdeğer bir anahtar arıyorsanız, bu yaklaşımı isteyebilirsiniz

session = boto3.session.Session()
resource = session.resource("s3")
bucket = resource.Bucket('mybucket')

key = 'dir-like-or-file-like-key'
objects = [o for o in bucket.objects.filter(Prefix=key).limit(1)]    
has_key = len(objects) > 0

Bu, bir üst anahtar veya dosyaya eşit olmayan bir anahtar veya var olmayan bir anahtar için çalışır. Yukarıda tercih edilen yaklaşımı denedim ve üst anahtarlarda başarısız oldum.


0

Sadece istisna yakalamak için botocore.exceptions.ClientErrorbotocore yüklememiz gerektiğini fark ettim . botocore 36M disk alanı kaplar. Aws lambda fonksiyonlarını kullanırsak bu özellikle etkilidir. Bunun yerine, sadece istisna kullanırsak ekstra kütüphaneyi kullanarak atlayabiliriz!

  • Dosya uzantısının '.csv' olduğunu doğrulıyorum
  • Kova yoksa bu bir istisna oluşturmaz!
  • Kova varsa ancak nesne yoksa bu bir istisna atmaz!
  • Kova boşsa bu bir istisna atar!
  • Kepçenin izni yoksa bu bir istisna atar!

Kod şuna benzer. Lütfen düşüncelerinizi paylaşın:

import boto3
import traceback

def download4mS3(s3bucket, s3Path, localPath):
    s3 = boto3.resource('s3')

    print('Looking for the csv data file ending with .csv in bucket: ' + s3bucket + ' path: ' + s3Path)
    if s3Path.endswith('.csv') and s3Path != '':
        try:
            s3.Bucket(s3bucket).download_file(s3Path, localPath)
        except Exception as e:
            print(e)
            print(traceback.format_exc())
            if e.response['Error']['Code'] == "404":
                print("Downloading the file from: [", s3Path, "] failed")
                exit(12)
            else:
                raise
        print("Downloading the file from: [", s3Path, "] succeeded")
    else:
        print("csv file not found in in : [", s3Path, "]")
        exit(12)

AWS, python çalışma zamanlarının önceden yüklenmiş boto3
rinat.io

0

Sadece iş parçacığını takip eden biri, bir nesnenin S3'te var olup olmadığını kontrol etmenin en etkili yol olduğu sonucuna varabilir mi?

Head_object sadece gerçek nesnenin kendisinden daha hafif olan meta verileri kontrol ettiği için kazanabileceğini düşünüyorum



-1

Çıkış yapmak

bucket.get_key(
    key_name, 
    headers=None, 
    version_id=None, 
    response_headers=None, 
    validate=True
)

Grup içinde belirli bir anahtar olup olmadığını kontrol edin. Bu yöntem, anahtarın varlığını kontrol etmek için bir HEAD isteği kullanır. Döndürür: Bir Anahtar nesnesinin örneği veya Yok

dan Boto S3 Dokümanlar

Sadece bucket.get_key (anahtar adı) öğesini çağırabilir ve döndürülen nesnenin Yok olup olmadığını kontrol edebilirsiniz.


OP
MarkNS

AWS boto kütüphanesinin iki sürümü vardır. Bu yanıt, soru tarafından istenen sürümle çalışmaz.
MarkNS

OP için doğru bir cevap değil, ama bana yardımcı oluyor çünkü boto v2 kullanmam gerekiyor. Bu yüzden olumsuz oyu kaldırdım.
haͣrͬukaͣreͤrͬu
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.