Überprüfen Sie mit boto3, ob ein Schlüssel in einem Bucket in s3 vorhanden ist


163

Ich würde gerne wissen, ob in boto3 ein Schlüssel vorhanden ist. Ich kann den Inhalt des Eimers durchlaufen und den Schlüssel überprüfen, wenn er übereinstimmt.

Aber das scheint länger und ein Overkill. In den offiziellen Boto3-Dokumenten wird ausdrücklich angegeben, wie dies zu tun ist.

Vielleicht fehlt mir das Offensichtliche. Kann mir jemand zeigen, wie ich das erreichen kann?

Antworten:


194

Das boto.s3.key.KeyObjekt von Boto 2 verfügte früher über eine existsMethode, mit der überprüft wurde, ob der Schlüssel in S3 vorhanden war, indem eine HEAD-Anforderung ausgeführt und das Ergebnis angezeigt wurde. Es scheint jedoch, dass diese nicht mehr vorhanden ist. Du musst es selber machen:

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() führt eine HEAD-Anforderung für einen einzelnen Schlüssel durch, was schnell ist, selbst wenn das betreffende Objekt groß ist oder Sie viele Objekte in Ihrem Bucket haben.

Natürlich prüfen Sie möglicherweise, ob das Objekt vorhanden ist, weil Sie es verwenden möchten. Wenn dies der Fall ist, können Sie das einfach vergessen load()und ein get()oder download_file()direkt ausführen und dann den Fehlerfall dort behandeln.


Danke für die schnelle Antwort Wander. Ich brauche nur das gleiche für boto3.
Prabhakar Shanmugam

12
Denn boto3, so scheint es das Beste was Sie im Moment tun können , ist zu nennen , head_objectdie Metadaten für den Schlüssel , um zu versuchen und zu holen, dann die resultierenden Fehler behandeln , wenn sie noch nicht existiert.
Wander Nauta

1
@Leonid Das könnten Sie sicherlich, aber nur, wenn Sie dies in eine Funktion oder Methode einwickeln, die Ihnen überlassen bleibt. Ich habe den Beispielcode ein wenig geändert, damit der existsBoolesche Wert weg ist, und es ist klarer (ich hoffe!), Dass die Leute dies an ihre Situation anpassen sollen.
Wander Nauta

2
-1; funktioniert bei mir nicht Auf boto3 Version 1.5.26 sehe e.response['Error']['Code']ich einen Wert wie "NoSuchKey", nicht "404". Ich habe nicht überprüft, ob dies auf einen Unterschied in den Bibliotheksversionen oder auf eine Änderung in der API selbst zurückzuführen ist, seit diese Antwort geschrieben wurde. In beiden Fällen besteht in meiner Version von boto3 ein kürzerer Ansatz als das Überprüfen e.response['Error']['Code']darin, nur s3.meta.client.exceptions.NoSuchKeyan erster Stelle zu fangen .
Mark Amery

2
Wenn Sie ein s3 client(im Gegensatz zu einem resource) verwenden, tun Sie dies s3.head_object(Bucket='my_bucket', Key='my_key')anstelle vons3.Object(...).load()
user2426679

125

Ich bin kein großer Fan von Ausnahmen für den Kontrollfluss. Dies ist ein alternativer Ansatz, der in boto3 funktioniert:

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

Danke für das Update EvilPuppetMaster. Leider hatte ich bei meiner letzten Überprüfung keine Zugriffsrechte für Listen-Buckets. Ihre Antwort passt zu meiner Frage, also habe ich Sie gewählt. Aber ich hatte die erste Antwort schon lange vorher als Antwort markiert. Danke für Ihre Hilfe.
Prabhakar Shanmugam

27
Zählt dies nicht als Listungsanfrage (12,5x teurer als get)? Wenn Sie dies für 100 Millionen Objekte tun, könnte das etwas teuer werden ... Ich habe das Gefühl, dass die Fangausnahmemethode leider die bisher beste ist.
Pierre D

21
Die Liste kann pro Anforderung 12,5-mal so teuer sein, aber eine einzelne Anforderung kann auch 100 Millionen Objekte zurückgeben, wobei ein einzelner Abruf nur eines zurückgeben kann. In Ihrem hypothetischen Fall wäre es also billiger, alle 100 Millionen mit Liste abzurufen und dann lokal zu vergleichen, als 100 Millionen Einzelabrufe durchzuführen. Ganz zu schweigen von der 1000-fachen Geschwindigkeit, da Sie den http-Roundtrip nicht für jedes Objekt benötigen würden.
EvilPuppetMaster

Es funktioniert nicht, wenn sich meine Datei in Ordnern innerhalb eines s3-
Buckets befindet

2
@ user3186866 Das liegt daran, dass S3 eigentlich keine "Ordner" hat. Alle Objekte existieren als Dateien auf ihren angegebenen Pfaden. Ordner sind ein Werkzeug, mit dem wir die Struktur unseres Speichers organisieren und verstehen können. In Wirklichkeit sind S3-Buckets genau das, Buckets.
ibtokin

113

Der einfachste (und wahrscheinlich effizienteste) Weg, den ich gefunden habe, ist folgender:

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
Hinweis: Sie müssen aws_access_key_id / aws_secret_access_key usw. nicht übergeben, wenn Sie eine Rolle verwenden oder die Schlüssel in Ihrer .aws-Konfiguration haben, können Sie dies einfach tuns3 = boto3.client('s3')
Andy Hayden

20
Ich denke, das Hinzufügen dieses Tests gibt Ihnen ein wenig mehr Sicherheit, dass das Objekt wirklich nicht existiert, als ein anderer Fehler, der die Ausnahme auslöst. Beachten Sie, dass 'e' die ClientError-Ausnahmeinstanz ist:if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
Richard

@AndyHayden Was würde jeder Versuch in Bezug auf die aws-Kosten zählen?
Schleife

2
@ Taylor ist eine Get-Anfrage, aber ohne Datenübertragung.
Andy Hayden

1
ClientError ist ein Haken für 400, nicht nur für 404, daher ist es nicht robust.
Mickzer

21

Wenn Sie in Boto3 mithilfe von list_objects nach einem Ordner (Präfix) oder einer Datei suchen. Sie können das Vorhandensein von 'Inhalt' im Antwortdiktat verwenden, um zu überprüfen, ob das Objekt vorhanden ist. Es ist eine andere Möglichkeit, die Versuche / Ausnahmen zu vermeiden, wie @EvilPuppetMaster vorschlägt

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

2
Hatte ein Problem damit. list_objects ("2000") gibt Schlüssel wie "2000-01", "2000-02"
Gunnar Cheng

3
Dies gibt nur bis zu 1000 Objekte zurück! boto3.amazonaws.com/v1/documentation/api/latest/reference/…
RoachLord

Dies ist die effizienteste Lösung, da hierfür s3:GetObjectnur die s3:ListBucketBerechtigungen erforderlich sind
Vishrant,

11

Nicht nur, clientsondern bucketauch:

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
Möglicherweise möchten Sie das Objekt nicht abrufen, sondern nur prüfen, ob es vorhanden ist. Sie können eine Methode verwenden, die das Objekt wie andere Beispiele hier leitet, z bucket.Object(key).last_modified.
Ryanjdillon

10

Sie können S3Fs verwenden , bei denen es sich im Wesentlichen um einen Wrapper um boto3 handelt, der typische Operationen im Dateisystemstil verfügbar macht:

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

Obwohl ich denke, dass dies funktionieren würde, stellt sich die Frage, wie dies mit boto3 gemacht werden soll. In diesem Fall ist es praktisch, das Problem zu lösen, ohne eine zusätzliche Bibliothek zu installieren.
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, hier sind die sehr einfachen Funktionen, die ich benutze

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
Dies ist die einzige Antwort, die ich sah, die sich mit der Überprüfung der Existenz eines 'Ordners' im Vergleich zu einer 'Datei' befasste. Dies ist besonders wichtig für Routinen, die wissen müssen, ob ein bestimmter Ordner vorhanden ist, nicht die bestimmten Dateien in einem Ordner.
Dave Campbell

Dies ist zwar eine sorgfältige Antwort, aber nur dann nützlich, wenn der Benutzer versteht, dass der Begriff eines Ordners in diesem Fall irreführend ist. In S3 kann ein leerer 'Ordner' in einem Bucket vorhanden sein. Wenn dies der Fall ist, gibt isdir_s3 False zurück. Ich habe ein paar Minuten gebraucht, um das zu klären. Ich habe darüber nachgedacht, die Antwort so zu bearbeiten, als ob der Ausdruck auf> 0 geändert würde Ergebnis, das Sie erwarten
PyNEwbie

5

Angenommen, Sie möchten nur überprüfen, ob ein Schlüssel vorhanden ist (anstatt ihn leise zu überschreiben), führen Sie zuerst diese Überprüfung durch:

import boto3
s3_client = boto3.client('s3')
mykey = 'someprefix/myfile-abc123'
mybucket = 'my-bucket-name'
response = s3_client.list_objects_v2(Bucket=mybucket, Prefix=mykey)
if response.get('Contents'):
    print("key exists")
else:
    print("safe to put new bucket object 'mykey'")
    # 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

Versuchen Sie dies einfach

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

Dies könnte sowohl das Präfix als auch den Schlüssel überprüfen und höchstens 1 Schlüssel abrufen.

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

Wenn Sie weniger als 1000 in einem Verzeichnis oder Bucket haben, können Sie diese festlegen und nach Überprüfung prüfen, ob ein solcher Schlüssel in dieser Gruppe enthalten ist:

files_in_dir = {d['Key'].split('/')[-1] for d in s3_client.list_objects_v2(
Bucket='mybucket',
Prefix='my/dir').get('Contents') or []}

Ein solcher Code funktioniert auch dann, wenn er my/dirnicht vorhanden ist.

http://boto3.readthedocs.io/en/latest/reference/services/s3.html#S3.Client.list_objects_v2


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

Für boto3 kann ObjectSummary verwendet werden, um zu überprüfen, ob ein Objekt vorhanden ist.

Enthält die Zusammenfassung eines Objekts, das in einem Amazon S3-Bucket gespeichert ist. Dieses Objekt enthält nicht die vollständigen Metadaten des Objekts oder einen seiner Inhalte

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

Ruft s3.Client.head_object auf, um die Attribute der ObjectSummary-Ressource zu aktualisieren.

Dies zeigt, dass Sie ObjectSummaryanstelle von verwenden können, Objectwenn Sie nicht verwenden möchten get(). Die load()Funktion ruft das Objekt nicht ab, sondern erhält nur die Zusammenfassung.


1

Hier ist eine Lösung, die für mich funktioniert. Eine Einschränkung ist, dass ich das genaue Format des Schlüssels im Voraus kenne, sodass ich nur die einzelne Datei aufführe

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

Sie können dafür Boto3 verwenden.

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

Hier ist der Schlüssel der Pfad, den Sie überprüfen möchten, vorhanden oder nicht


Aus einem einfachen %timeitTest scheint dies die schnellste Option zu sein
Itamar Katz

1

Mit der get()Methode ist es wirklich einfach

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

Nicht robust, Ausnahme könnte aus vielen Gründen ausgelöst werden, z. B. HTTP 500, und dieser Code würde eine 404 annehmen.
mickzer

Wir benötigen jedoch Informationen darüber, ob auf die Datei zugegriffen werden kann oder nicht. Wenn es existiert und nicht zugänglich ist, ist es gleichbedeutend mit nicht existieren. richtig?
Isambitd

@ Mickzer überprüfen Sie die Änderungen jetzt.
Isambitd

1
Um auf Ihren vorherigen Kommentar zu antworten: Nein, das Verhalten bei einem HTTP 500 könnte darin bestehen, es erneut zu versuchen, ein 401/403, um die Authentifizierung zu beheben usw. Es ist wichtig, nach dem tatsächlichen Fehlercode zu suchen.
Mickzer

0

Es gibt eine einfache Möglichkeit, um zu überprüfen, ob eine Datei im S3-Bucket vorhanden ist oder nicht. Wir brauchen dafür keine Ausnahme zu verwenden

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

Dies ist falsch, wenn eine Datei, die mit beginnt object_name, im Bucket vorhanden ist. ZB my_file.txt.oldversionwird ein falsches Positiv zurückgegeben, wenn Sie nach suchen my_file.txt. Für die meisten ein Randfall, aber für etwas so Breites wie "Existiert die Datei", das Sie wahrscheinlich in Ihrer gesamten Anwendung verwenden, ist es wahrscheinlich wert, berücksichtigt zu werden.
Andrew Schwartz

0

Wenn Sie einen Schlüssel suchen, der einem Verzeichnis entspricht, möchten Sie möglicherweise diesen Ansatz

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

Dies funktioniert für einen übergeordneten Schlüssel oder einen Schlüssel, der einer Datei oder einem nicht vorhandenen Schlüssel entspricht. Ich habe den oben beschriebenen bevorzugten Ansatz ausprobiert und bin bei den übergeordneten Schlüsseln fehlgeschlagen.


0

Ich habe festgestellt, dass botocore.exceptions.ClientErrorwir nur Botocore installieren müssen, um die Ausnahme mit zu fangen. Botocore belegt 36 Millionen Speicherplatz. Dies wirkt sich besonders aus, wenn wir aws-Lambda-Funktionen verwenden. Wenn wir nur eine Ausnahme verwenden, können wir stattdessen die zusätzliche Bibliothek überspringen!

  • Ich überprüfe, ob die Dateierweiterung ".csv" ist.
  • Dies löst keine Ausnahme aus, wenn der Bucket nicht vorhanden ist!
  • Dies löst keine Ausnahme aus, wenn der Bucket vorhanden ist, das Objekt jedoch nicht vorhanden ist!
  • Dies löst eine Ausnahme aus, wenn der Eimer leer ist!
  • Dies löst eine Ausnahme aus, wenn der Bucket keine Berechtigungen hat!

Der Code sieht so aus. Bitte teilen Sie Ihre Gedanken:

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 sagt, dass Python-Laufzeiten mit vorinstalliertem Boto3
geliefert werden

0

Kann jemand, der dem Thread folgt, feststellen, welcher der effizienteste Weg ist, um zu überprüfen, ob ein Objekt in S3 vorhanden ist?

Ich denke, head_object könnte gewinnen, da es nur die Metadaten überprüft, die leichter sind als das eigentliche Objekt



-1

Auschecken

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

Überprüfen Sie, ob ein bestimmter Schlüssel im Bucket vorhanden ist. Diese Methode verwendet eine HEAD-Anforderung, um die Existenz des Schlüssels zu überprüfen. Rückgabe: Eine Instanz eines Schlüsselobjekts oder Keine

von Boto S3 Docs

Sie können einfach buck.get_key (Schlüsselname) aufrufen und prüfen, ob das zurückgegebene Objekt None ist.


Dies funktioniert nicht mit boto3, wie vom OP
MarkNS

Es gibt zwei Versionen der AWS-Botobibliothek. Diese Antwort funktioniert nicht mit der Version, die von der Frage angefordert wurde.
MarkNS

Es ist sicher keine richtige Antwort für OP, aber es hilft mir, weil ich Boto v2 verwenden muss. Deshalb habe ich eine negative Abstimmung entfernt.
haͣrͬukaͣreͤrͬu
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.