Ü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?

Prabhakar Shanmugam
quelle

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.

Wandern Sie Nauta
quelle
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")
EvilPuppetMaster
quelle
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
o_c
quelle
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
Lucian Thorr
quelle
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')
Vitaly Zdanevich
quelle
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')
VinceP
quelle
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)
Vivek
quelle
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
Andy Reagan
quelle
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...
Marvls
quelle
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")
Alkesh Mahajan
quelle
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
Fang Zhang
quelle
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

Vitaly Zdanevich
quelle
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

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

Veedka
quelle
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."
Rush S.
quelle
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

AshuGG
quelle
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')) 
isambitd
quelle
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")
Mahesh Mogal
quelle
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.

Peter Kahn
quelle
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)
Benutzer 923227
quelle
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

Sai
quelle
0

Unter https://www.peterbe.com/plog/fastest-way-to-find-out-if-a-file-exists-in-s3 wird darauf hingewiesen, dass dies die schnellste Methode ist:

import boto3

boto3_session = boto3.session.Session()
s3_session_client = boto3_session.client("s3")
response = s3_session_client.list_objects_v2(
    Bucket=bc_df_caches_bucket, Prefix=s3_key
)
for obj in response.get("Contents", []):
    if obj["Key"] == s3_key:
        return True
return False
ivansabik
quelle
-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.

Alexander Truslow
quelle
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