sprawdź, czy klucz istnieje w wiadrze w s3 za pomocą boto3


164

Chciałbym wiedzieć, czy klucz istnieje w boto3. Mogę zapętlić zawartość wiadra i sprawdzić, czy klucz pasuje.

Ale to wydaje się dłuższe i przesada. Oficjalna dokumentacja Boto3 wyraźnie określa, jak to zrobić.

Może brakuje mi tego, co oczywiste. Czy ktoś może mi wskazać, jak mogę to osiągnąć.

Odpowiedzi:


195

Obiekt Boto 2 miał boto.s3.key.Keykiedyś existsmetodę, która sprawdzała, czy klucz istnieje na S3, wykonując żądanie HEAD i patrząc na wynik, ale wydaje się, że już go nie ma. Musisz to zrobić sam:

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() wykonuje żądanie HEAD dla pojedynczego klucza, co jest szybkie, nawet jeśli dany obiekt jest duży lub masz wiele obiektów w swoim zasobniku.

Oczywiście możesz sprawdzić, czy obiekt istnieje, ponieważ planujesz go użyć. Jeśli tak jest, możesz po prostu zapomnieć o load()i zrobić get()lub download_file()bezpośrednio, a następnie obsłużyć tam przypadek błędu.


Dzięki za szybką odpowiedź Wander. Po prostu potrzebuję tego samego dla boto3.
Prabhakar Shanmugam

12
boto3Wydaje się bowiem , że najlepsze, co możesz teraz zrobić, to zadzwonić, head_objectaby spróbować pobrać metadane dla klucza, a następnie obsłużyć wynikowy błąd, jeśli nie istnieje.
Wander Nauta

1
@Leonid Z pewnością możesz, ale tylko wtedy, gdy zapakujesz to w funkcję lub metodę, która zależy od Ciebie. Zmodyfikowałem nieco przykładowy kod tak, że nie ma wartości existslogicznej i jest jaśniejsze (mam nadzieję!), Że ludzie powinni dostosować to do swojej sytuacji.
Wander Nauta

2
-1; nie działa dla mnie. W wersji 1.5.26 boto3 widzę e.response['Error']['Code']wartość taką jak "NoSuchKey"nie "404". Nie sprawdzałem, czy jest to spowodowane różnicą w wersjach biblioteki, czy zmianą samego API od czasu napisania tej odpowiedzi. Tak czy inaczej, w mojej wersji boto3 krótszym podejściem niż sprawdzanie e.response['Error']['Code']jest złapanie tylko s3.meta.client.exceptions.NoSuchKeyw pierwszej kolejności.
Mark Amery,

2
jeśli używasz s3 client(w przeciwieństwie do a resource), zrób s3.head_object(Bucket='my_bucket', Key='my_key')zamiasts3.Object(...).load()
user2426679

126

Nie jestem wielkim fanem używania wyjątków dla przepływu sterowania. To jest alternatywne podejście, które działa w boto3:

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

Dzięki za aktualizację EvilPuppetMaster. Niestety, kiedy ostatnio sprawdzałem, nie miałem praw dostępu do zasobnika listy. Twoja odpowiedź jest odpowiednia dla mojego pytania, więc zagłosowałem na Ciebie. Ale już wcześniej oznaczyłem pierwszą odpowiedź jako odpowiedź. Dzięki za pomoc.
Prabhakar Shanmugam,

27
Czy to nie liczy się jako prośba o wpis (12,5 razy droższa niż cena)? Jeśli zrobisz to dla 100 milionów obiektów, może to być trochę drogie ... Mam wrażenie, że metoda łapania wyjątków jest niestety najlepsza do tej pory.
Pierre D

21
Lista może być 12,5 razy droższa na żądanie, ale pojedyncze żądanie może również zwrócić 100 milionów obiektów, podczas gdy pojedyncze pobranie może zwrócić tylko jeden. Więc w twoim hipotetycznym przypadku byłoby taniej pobrać wszystkie 100 milionów za pomocą listy, a następnie porównać je lokalnie, niż zrobić 100 milionów indywidualnych wyników. Nie wspominając o 1000x szybciej, ponieważ nie potrzebowałbyś podróży w obie strony http dla każdego obiektu.
EvilPuppetMaster

Nie działa, gdy mój plik znajduje się w folderach w
zasobniku

2
@ user3186866 To dlatego, że S3 tak naprawdę nie ma „folderów”. Wszystkie obiekty istnieją jako pliki w podanych ścieżkach. Foldery to narzędzie, które pomaga nam organizować i rozumieć strukturę naszego magazynu, ale w rzeczywistości wiadra S3 to po prostu wiadra.
ibtokin,

114

Najłatwiejszy sposób, jaki znalazłem (i prawdopodobnie najbardziej wydajny), to:

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
Uwaga: nie musisz przekazywać aws_access_key_id / aws_secret_access_key itp., Jeśli używasz roli lub masz klucze w konfiguracji .aws, możesz po prostu to zrobićs3 = boto3.client('s3')
Andy Hayden

20
Myślę, że dodanie tego testu daje trochę więcej pewności, że obiekt naprawdę nie istnieje, a nie jakiś inny błąd powodujący wyjątek - zwróć uwagę, że „e” jest wystąpieniem wyjątku ClientError:if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
Richard

@AndyHayden Ile liczyłaby się każda próba pod względem kosztu aws?
pętla

2
@Taylor to żądanie pobrania, ale bez przesyłania danych.
Andy Hayden

1
ClientError to haczyk dla wszystkich 400, a nie tylko 404, dlatego nie jest solidny.
mickzer

21

W Boto3, jeśli sprawdzasz folder (prefiks) lub plik za pomocą list_objects. Możesz użyć istnienia „Contents” w dyktandzie odpowiedzi jako sprawdzenia, czy obiekt istnieje. Jest to inny sposób na uniknięcie prób / wyjątków, jak sugeruje @EvilPuppetMaster

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

2
Miałem z tym problem. list_objects ("2000") zwróci klucze, takie jak "2000-01", "2000-02"
Gunnar Cheng


Jest to najbardziej wydajne rozwiązanie, ponieważ nie wymaga s3:GetObjectuprawnień tylko s3:ListBucketuprawnienia
Vishrant

11

Nie tylko, clientale bucketteż:

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
Możesz nie chcieć dostać obiektu, ale po prostu sprawdź, czy tam jest. Możesz użyć metody, która kieruje obiektem, podobnie jak inne przykłady tutaj, takie jak bucket.Object(key).last_modified.
ryanjdillon

10

Możesz użyć S3F , który jest zasadniczo opakowaniem wokół boto3, które ujawnia typowe operacje w stylu systemu plików:

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

Chociaż myślę, że to zadziała, pytanie dotyczy tego, jak to zrobić z boto3; w takim przypadku praktyczne jest rozwiązanie problemu bez instalowania dodatkowej biblioteki.
paulkernfeld

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, oto bardzo proste funkcje, których używam

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
jest to jedyna odpowiedź, jaką widziałem, która dotyczyła sprawdzania istnienia „folderu” w porównaniu z „plikiem”. jest to bardzo ważne w przypadku procedur, które muszą wiedzieć, czy istnieje określony folder, a nie określone pliki w folderze.
Dave Campbell

Chociaż jest to ostrożna odpowiedź, jest przydatna tylko wtedy, gdy użytkownik zrozumie, że pojęcie folderu jest w tym przypadku mylące. Pusty `` folder '' może istnieć w S3 wewnątrz wiadra, a jeśli tak, isdir_s3 zwróci wartość Fałsz. Zajęło mi to kilka minut, zanim to uporządkowałem. Myślałem o edycji odpowiedzi tak, jakby wyrażenie zostało zmienione na> 0, otrzymasz wynik, jakiego oczekujesz
PyNEwbie

5

Zakładając, że chcesz tylko sprawdzić, czy klucz istnieje (zamiast po cichu go nadpisywać), najpierw zrób to sprawdzenie:

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

3

Wypróbuj to proste

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

3

Może to sprawdzić zarówno prefiks, jak i klucz i pobrać maksymalnie 1 klucz.

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


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

W przypadku boto3 ObjectSummary może służyć do sprawdzenia, czy obiekt istnieje.

Zawiera podsumowanie obiektu przechowywanego w zasobniku Amazon S3. Ten obiekt nie zawiera pełnych metadanych obiektu ani żadnej jego zawartości

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

W ObjectSummary.load

Wywołuje s3.Client.head_object, aby zaktualizować atrybuty zasobu ObjectSummary.

To pokazuje, że możesz użyć ObjectSummaryzamiast, Objectjeśli planujesz nie używać get(). load()Funkcja nie odebrania obiektu to tylko uzyska podsumowanie.


1

Oto rozwiązanie, które działa dla mnie. Jedynym zastrzeżeniem jest to, że z wyprzedzeniem znam dokładny format klucza, więc wymieniam tylko jeden plik

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

możesz do tego użyć Boto3.

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!")

Tutaj kluczem jest ścieżka, którą chcesz sprawdzić, czy istnieje, czy nie


Z prostego %timeittestu wydaje się, że jest to najszybsza opcja
Itamar Katz

1

get()Metoda jest naprawdę prosta

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

Niezawodne, wyjątek można zgłosić z wielu powodów, np. HTTP 500, a ten kod
założyłby

Ale potrzebujemy informacji o tym, czy plik jest dostępny, czy nie. Jeśli istnieje i nie może być dostępne, oznacza to, że nie istnieje. dobrze?
isambitd

@mickzer sprawdź zmiany teraz.
isambitd

1
Aby odpowiedzieć na poprzedni komentarz, nie, zachowanie na HTTP 500 może polegać na ponownej próbie, 401/403 w celu naprawienia autoryzacji itp. Ważne jest, aby sprawdzić rzeczywisty kod błędu.
mickzer

0

Jest jeden prosty sposób, dzięki któremu możemy sprawdzić, czy plik istnieje w zasobniku S3, czy nie. Nie musimy do tego używać wyjątków

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

Będzie to nieprawidłowe, jeśli plik zaczynający się od object_nameistnieje w zasobniku. Np. Zwróci my_file.txt.oldversionfałszywie dodatni wynik, jeśli sprawdzisz my_file.txt. Dla większości jest to trochę skrajna sprawa, ale w przypadku czegoś tak szerokiego, jak „czy plik istnieje”, którego prawdopodobnie użyjesz w swojej aplikacji, prawdopodobnie warto wziąć pod uwagę.
Andrew Schwartz,

0

Jeśli szukasz klucza, który jest odpowiednikiem katalogu, możesz chcieć tego podejścia

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

Działa to dla klucza nadrzędnego lub klucza równego plikowi lub klucza, który nie istnieje. Wypróbowałem preferowane podejście powyżej i zawiodłem na kluczach nadrzędnych.


0

Zauważyłem, że aby złapać wyjątek za pomocą botocore.exceptions.ClientError, musimy zainstalować botocore. botocore zajmuje 36M miejsca na dysku. Ma to szczególne znaczenie, jeśli używamy funkcji lambda aws. Zamiast tego, jeśli użyjemy tylko wyjątku, możemy pominąć korzystanie z dodatkowej biblioteki!

  • Sprawdzam, czy rozszerzenie pliku to „.csv”
  • Nie spowoduje to zgłoszenia wyjątku, jeśli zasobnik nie istnieje!
  • Nie spowoduje to zgłoszenia wyjątku, jeśli zasobnik istnieje, ale obiekt nie istnieje!
  • Spowoduje to odrzucenie wyjątku, jeśli wiadro jest puste!
  • Spowoduje to wyrzucenie wyjątku, jeśli zasobnik nie ma uprawnień!

Kod wygląda następująco. Podziel się swoimi przemyśleniami:

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 mówi, że środowiska wykonawcze Pythona są dostarczane z preinstalowanym boto3
rinat.io

0

Czy po prostu podążając za wątkiem, ktoś może stwierdzić, który z nich jest najbardziej efektywnym sposobem sprawdzenia, czy obiekt istnieje w S3?

Myślę, że head_object może wygrać, ponieważ sprawdza tylko metadane, które są jaśniejsze niż sam obiekt



-1

Sprawdzić

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

Sprawdź, czy w zasobniku istnieje określony klucz. Ta metoda używa żądania HEAD do sprawdzenia istnienia klucza. Zwraca: wystąpienie obiektu Key lub None

z Boto S3 Docs

Możesz po prostu zadzwonić do bucket.get_key (nazwa klucza) i sprawdzić, czy zwrócony obiekt ma wartość Brak.


To nie działa z boto3, zgodnie z żądaniem OP
MarkNS

Istnieją dwie wersje biblioteki AWS Boto. Ta odpowiedź nie działa z wersją, której zażądało pytanie.
MarkNS,

To z pewnością nie jest poprawna odpowiedź na OP, ale pomaga mi, ponieważ muszę używać boto v2. Dlatego usunąłem głos negatywny.
haͣrͬukaͣreͤrͬu
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.