controlla se esiste una chiave in un bucket in s3 usando boto3


165

Vorrei sapere se esiste una chiave in boto3. Posso eseguire il loop del contenuto del bucket e controllare la chiave se corrisponde.

Ma questo sembra più lungo e eccessivo. I documenti ufficiali di Boto3 affermano esplicitamente come farlo.

Forse mi manca l'ovvio. Qualcuno può indicarmi come posso raggiungere questo obiettivo.

Risposte:


196

L' boto.s3.key.Keyoggetto di Boto 2 aveva un existsmetodo che controllava l'esistenza della chiave su S3 eseguendo una richiesta HEAD e osservando il risultato, ma sembra che non esista più. Devi farlo da solo:

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() fa una richiesta HEAD per una singola chiave, che è veloce, anche se l'oggetto in questione è grande o se hai molti oggetti nel tuo bucket.

Certo, potresti verificare se l'oggetto esiste perché stai pianificando di usarlo. Se questo è il caso, puoi semplicemente dimenticare load()e fare un get()o download_file()direttamente, quindi gestire il caso di errore lì.


Grazie per la rapida risposta Wander. Ho solo bisogno dello stesso per boto3.
Prabhakar Shanmugam,

12
Per boto3, sembra che il meglio che puoi fare al momento sia chiamare head_objectper provare a recuperare i metadati per la chiave, quindi gestire l'errore risultante se non esiste.
Passeggia per Nauta il

1
@Leonid Certamente potresti, ma solo se lo avessi avvolto in una funzione o metodo, che dipende da te. Ho modificato un po 'il codice di esempio in modo che il existsbooleano sia sparito, ed è più chiaro (spero!) Che le persone dovrebbero adattarlo alla loro situazione.
Passeggia per Nauta l'

2
-1; non funziona per me. Sulla versione 1.5.26 di boto3 vedo e.response['Error']['Code']avere un valore simile "NoSuchKey", no "404". Non ho verificato se ciò è dovuto a una differenza nelle versioni della libreria o a una modifica dell'API stessa da quando è stata scritta questa risposta. Ad ogni modo, nella mia versione di boto3, un approccio più breve rispetto al controllo e.response['Error']['Code']è quello di catturare solo s3.meta.client.exceptions.NoSuchKeyin primo luogo.
Mark Amery,

2
se stai usando un s3 client(al contrario di a resource), fai s3.head_object(Bucket='my_bucket', Key='my_key')invece dis3.Object(...).load()
user2426679,

127

Non sono un grande fan dell'uso delle eccezioni per il flusso di controllo. Questo è un approccio alternativo che funziona in 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")

Grazie per l'aggiornamento EvilPuppetMaster. Sfortunatamente, quando ho controllato l'ultima volta, non avevo i diritti di accesso al bucket. La tua risposta è adatta alla mia domanda, quindi ti ho votato. Ma avevo già contrassegnato la prima risposta come risposta molto prima. Grazie per l'aiuto.
Prabhakar Shanmugam,

27
Questo non conta come una richiesta di quotazione (12,5 volte più costosa di quella ottenuta)? Se lo fai per 100 milioni di oggetti, potrebbe essere un po 'costoso ... Ho la sensazione che il metodo dell'eccezione di cattura sia purtroppo il migliore finora.
Pierre D,

21
L'elenco può essere 12,5 volte più costoso per richiesta, ma una singola richiesta può anche restituire 100 milioni di oggetti in cui un singolo get può restituire solo uno. Quindi, nel tuo ipotetico caso, sarebbe più economico recuperare tutti i 100 milioni con la lista e poi confrontarli localmente, piuttosto che fare 100m. Per non parlare di 1000 volte più veloce poiché non è necessario il round trip http per ogni oggetto.
EvilPuppetMaster

Non funziona quando il mio file si trova all'interno di cartelle all'interno di un bucket s3
user3186866

2
@ user3186866 Questo perché S3 in realtà non ha "cartelle". Tutti gli oggetti esistono come file nei rispettivi percorsi. Le cartelle sono uno strumento che ci aiuta a organizzare e comprendere la struttura del nostro storage, ma in realtà i bucket S3 sono proprio questo, bucket.
ibtokin,

114

Il modo più semplice che ho trovato (e probabilmente il più efficiente) è questo:

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
Nota: non devi passare aws_access_key_id / aws_secret_access_key ecc. Se usi un ruolo o hai le chiavi nella tua configurazione .aws, puoi semplicemente farlos3 = boto3.client('s3')
Andy Hayden,

20
Penso che l'aggiunta di questo test ti dia un po 'più di fiducia in realtà l'oggetto non esiste, piuttosto che qualche altro errore che solleva l'eccezione - nota che' e 'è l'istanza dell'eccezione ClientError:if e.response['ResponseMetadata']['HTTPStatusCode'] == 404:
Richard

@AndyHayden Cosa proverebbe ciascuno in termini di costi di aws?
loop

2
@Taylor è una richiesta get ma senza trasferimento di dati.
Andy Hayden,

1
ClientError è un problema per 400, non solo per 404, quindi non è robusto.
Mickzer

21

In Boto3, se stai cercando una cartella (prefisso) o un file usando list_objects. È possibile utilizzare l'esistenza di "Contenuti" nel dict di risposta come controllo dell'esistenza dell'oggetto. È un altro modo per evitare il tentativo / tranne le catture come suggerisce @EvilPuppetMaster

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

2
Ho avuto un problema in questo. list_objects ("2000") restituirà chiavi come "2000-01", "2000-02"
Gunnar Cheng

3
Questo restituisce solo fino a 1000 oggetti! boto3.amazonaws.com/v1/documentation/api/latest/reference/…
RoachLord

Questa è la soluzione più efficiente in quanto non richiede s3:GetObjectautorizzazioni solo le s3:ListBucketautorizzazioni
Vishrant

11

Non solo clientma bucketanche:

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
Potresti non voler ottenere l'oggetto, ma solo vedere se è lì. È possibile utilizzare un metodo che dirige l'oggetto come altri esempi qui, come ad esempio bucket.Object(key).last_modified.
ryanjdillon,

10

È possibile utilizzare S3F , che è essenzialmente un wrapper per boto3 che espone le tipiche operazioni in stile file system:

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

Anche se penso che funzionerebbe, la domanda si chiede come fare con boto3; in questo caso, è pratico per risolvere il problema senza installare una libreria aggiuntiva.
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, ecco le funzioni molto semplici che sto usando

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
questa è l'unica risposta che ho visto indirizzata a verificare l'esistenza di una "cartella" rispetto a un "file". questo è estremamente importante per le routine che devono sapere se esiste una cartella specifica, non i file specifici in una cartella.
Dave Campbell,

Sebbene questa sia una risposta attenta, è utile solo se l'utente comprende che l'idea di una cartella è fuorviante in questo caso. In S3 può esistere una 'cartella' vuota all'interno di un bucket e in tal caso isdir_s3 restituirà False mi ci sono voluti un paio di minuti per risolverlo Stavo pensando di modificare la risposta come se l'espressione fosse cambiata in> 0 otterrai il risultato che ti aspetti
PyNEwbie,

5

Supponendo che tu voglia solo verificare se esiste una chiave (invece di sovrascriverla silenziosamente), fai prima questo controllo:

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

4

Questo potrebbe controllare sia il prefisso che la chiave e recupera al massimo 1 chiave.

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

Prova questo semplice

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

Per boto3, ObjectSummary può essere utilizzato per verificare l'esistenza di un oggetto.

Contiene il riepilogo di un oggetto archiviato in un bucket Amazon S3. Questo oggetto non contiene i metadati completi dell'oggetto o il suo contenuto

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

Chiama s3.Client.head_object per aggiornare gli attributi della risorsa ObjectSummary.

Ciò dimostra che è possibile utilizzare ObjectSummaryanziché Objectse si prevede di non utilizzare get(). La load()funzione non recupera l'oggetto ma ottiene solo il riepilogo.


1

Ecco una soluzione che funziona per me. Un avvertimento è che conosco in anticipo il formato esatto della chiave, quindi sto solo elencando il singolo file

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

puoi usare Boto3 per questo.

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

Qui chiave è il percorso che si desidera verificare esiste o meno


Da un semplice %timeittest questa sembra l'opzione più veloce
Itamar Katz,

1

È davvero semplice con il get()metodo

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

Non affidabile, l'eccezione potrebbe essere generata per molti motivi, ad esempio HTTP 500 e questo codice assumerebbe un 404.
Mickzer

Ma abbiamo bisogno di informazioni su se il file è accessibile o meno. Esiste e non può essere accessibile quindi equivale a non esistere. giusto?
Isambitd,

@mickzer controlla subito le modifiche.
Isambitd,

1
Per rispondere al commento precedente, No, il comportamento su un HTTP 500 potrebbe essere quello di riprovare, un 401/403 per correggere l'autenticazione ecc. È importante verificare il codice di errore effettivo.
Mickzer,

0

Esiste un modo semplice per verificare se il file esiste o meno nel bucket S3. Non è necessario utilizzare l'eccezione per questo

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

Ciò non sarà corretto se object_namenel bucket esiste un file che inizia con . Ad esempio my_file.txt.oldversionrestituirà un falso positivo se si controlla my_file.txt. Un po 'un caso limite per la maggior parte, ma per qualcosa di così ampio come "esiste il file" che probabilmente si utilizzerà in tutta l'applicazione probabilmente vale la pena prendere in considerazione.
Andrew Schwartz,

0

Se si cerca una chiave equivalente a una directory, è possibile che si desideri questo approccio

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

Funziona con una chiave padre o una chiave che equivale a un file o una chiave che non esiste. Ho provato l'approccio preferito sopra e non sono riuscito sulle chiavi padre.


0

Ho notato che solo per catturare l'eccezione usando botocore.exceptions.ClientErrordobbiamo installare botocore. botocore occupa 36M di spazio su disco. Ciò ha un impatto particolare se usiamo le funzioni di aws lambda. Al posto di ciò se usiamo solo l'eccezione, possiamo saltare usando la libreria extra!

  • Sto convalidando che l'estensione del file sia '.csv'
  • Ciò non genererà un'eccezione se il bucket non esiste!
  • Ciò non genererà un'eccezione se il bucket esiste ma l'oggetto non esiste!
  • Questo genera un'eccezione se il secchio è vuoto!
  • Questo genera un'eccezione se il bucket non ha autorizzazioni!

Il codice è simile al seguente. Per favore, condividi i tuoi pensieri:

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 afferma che i tempi di esecuzione di Python vengono forniti con boto3 preinstallato: docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html
rinat.io

0

Solo seguendo il thread, qualcuno può concludere qual è il modo più efficiente per verificare se esiste un oggetto in S3?

Penso che head_object potrebbe vincere in quanto controlla solo i metadati che sono più leggeri dell'oggetto reale stesso



-1

Check-out

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

Controlla se esiste una chiave particolare all'interno del bucket. Questo metodo utilizza una richiesta HEAD per verificare l'esistenza della chiave. Restituisce: un'istanza di un oggetto Key o None

da Boto S3 Docs

Puoi semplicemente chiamare bucket.get_key (nome chiave) e verificare se l'oggetto restituito è Nessuno.


Questo non funziona con boto3, come richiesto dall'OP
MarkNS

Esistono due versioni della libreria di boto di AWS. Questa risposta non funziona con la versione richiesta dalla domanda.
MarkNS,

Non è certo una risposta corretta per OP, ma mi aiuta perché devo usare boto v2. Ecco perché ho rimosso un voto negativo.
haͣrͬukaͣreͤrͬu
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.