Come verificare se una chiave specificata esiste in un determinato bucket S3 utilizzando Java


88

Vorrei verificare se esiste una chiave in un determinato bucket utilizzando Java. Ho esaminato l'API ma non ci sono metodi utili. Ho provato a usare getObjectma ha generato un'eccezione.


2
In futuro, fornisci maggiori informazioni, ad esempio qual è stata l'eccezione che hai ottenuto .. Ho fornito una risposta basata su
un'ipotesi

4
FYI: Per questa domanda, la risposta accettata non è la risposta migliore.
malana

Risposte:


3

Usa la libreria jets3t. È molto più semplice e robusto di AWS sdk. Usando questa libreria puoi chiamare, s3service.getObjectDetails (). Questo controllerà e recupererà solo i dettagli dell'oggetto (non il contenuto) dell'oggetto. Lancerà un 404 se l'oggetto è mancante. Quindi puoi catturare quell'eccezione e gestirla nella tua app.

Ma affinché funzioni, sarà necessario disporre dell'accesso ListBucket per l'utente su quel bucket. Solo l'accesso a GetObject non funzionerà. Il motivo è che Amazon ti impedirà di verificare la presenza della chiave se non hai accesso a ListBucket. In alcuni casi, anche solo sapere se una chiave è presente o meno sarà sufficiente per gli utenti malintenzionati. Quindi, a meno che non abbiano accesso a ListBucket, non saranno in grado di farlo.


4
Tutti - vedere una risposta aggiornata a questa domanda di seguito: stackoverflow.com/a/36653034/49678
alexandroid

3
jets3t è una vecchia libreria deprecata. Utilizza invece il file aws-java-sdk.
the_storyteller

"più facile e più robusto" è molto soggettivo
Leo Romanovsky

296

Ora c'è un metodo doesObjectExist nell'API Java ufficiale.

Godere!


13
È stato aggiunto in 1.10.51
piroscafo

5
Dobbiamo dare un voto positivo e portarlo in cima!
SureshS

2
La cosa giusta da fare sarebbe rendere questa la risposta accettata, ma solo l'OP può farlo. meta.stackexchange.com/questions/120568/…
malana

4
Questo deve fare una chiamata di rete, che è costosa se hai molti oggetti ... Peccato che non possa semplicemente restituire null sulla richiesta dei metadati.
Joel

9
Sembra che Amazon sia stato rimosso doesObjectExistdall'SDK 2.x (attualmente v2.3.9).
Bampfer

59

Aggiornare:

Sembra che ci sia una nuova API per controllare proprio questo. Vedi un'altra risposta in questa pagina: https://stackoverflow.com/a/36653034/435605

Post originale:

Uso errorCode.equals("NoSuchKey")

try {
    AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider());
    String bucketName = getBucketName();
    s3.createBucket(bucketName);
    S3Object object = s3.getObject(bucketName, getKey());
} catch (AmazonServiceException e) {
    String errorCode = e.getErrorCode();
    if (!errorCode.equals("NoSuchKey")) {
        throw e;
    }
    Logger.getLogger(getClass()).debug("No such key!!!", e);
}

Nota sull'eccezione: so che le eccezioni non dovrebbero essere utilizzate per il controllo del flusso. Il problema è che Amazon non ha fornito alcuna API per controllare questo flusso, ma solo la documentazione sull'eccezione.


14
Non utilizzare la gestione delle eccezioni per il controllo del programma.
Simon Peck

34
@ SimonPeck: hai ragione. Il problema è che Amazon non ha fornito alcuna API per controllare questo flusso, ma solo la documentazione sull'eccezione. Si prega di rimuovere il voto negativo se non di voto positivo.
AlikElzin-kilaka

1
Questo non sembra più essere vero per Java SDK. Vedo che my errorMessageè impostato su "Not Found", ma errorCodeè nullo.
bstempi

3
Vorrei cercare il codice di stato 404. Sembra più robusto che guardare una stringa
Oskar Kjellin

2
Il commento di @rboarman non è corretto, lo è NoSuchKey. Per un elenco definitivo dei codici di errore S3, consultare la documentazione: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George

23

Utilizzando l'SDK AWS, utilizza il metodo getObjectMetadata. Il metodo genererà un'eccezione AmazonServiceException se la chiave non esiste.

private AmazonS3 s3;
...
public boolean exists(String path, String name) {
    try {
        s3.getObjectMetadata(bucket, getS3Path(path) + name); 
    } catch(AmazonServiceException e) {
        return false;
    }
    return true;
}

2
getObject genera anche AmazonServiceException, quindi perché due chiamate? Inoltre, come faccio a sapere che l'oggetto non esiste da questa excpetion? Forse è stato a causa di un altro errore S3 e l'oggetto è stato effettivamente trovato.
AlikElzin-kilaka

5
Non utilizzare la gestione delle eccezioni per il controllo del programma.
Simon Peck

4
@ AlikElzin-kilaka, perché getObject () significa che devi scaricare il contenuto dell'oggetto, che potrebbe essere enorme.
Jason Nichols

18
@SimonPeck, non è l'ideale, ma quando Amazon offre un metodo exist () appropriato, il tuo punto è valido.
Jason Nichols

4
@SimonPeck hai un'alternativa in questo caso? Questo non è un palese abuso di eccezioni come flusso di controllo del programma ... questo è semplice, accurato in ciò che fa e sicuro. Se porti la tua idea all'estremo (come apparentemente sei se pensi che questo frammento di codice stia abusando delle eccezioni), allora perché avere eccezioni in una lingua? Piuttosto che lanciare un'eccezione per avvisare il programma e modificare il flusso del programma , suppongo che il runtime dovrebbe terminare.
Don Cheadle

17

In Amazon Java SDK 1.10+, puoi utilizzare getStatusCode()per ottenere il codice di stato della risposta HTTP, che sarà 404 se l'oggetto non esiste.

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import org.apache.http.HttpStatus;

try {
    AmazonS3 s3 = new AmazonS3Client();
    ObjectMetadata object = s3.getObjectMetadata("my-bucket", "my-client");
} catch (AmazonS3Exception e) {
    if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
        // bucket/key does not exist 
    } else {
        throw e;
    }
}

getObjectMetadata()consuma meno risorse e la risposta non deve essere chiusa come getObject().


Nelle versioni precedenti, è possibile utilizzare getErrorCode()e verificare la stringa appropriata (dipende dalla versione).


Se al tuo oggetto s3 non sono allegati metadati, getObjectMetadata genererà un errore 404 anche se l'oggetto s3 esiste. Non lo consiglierò se l'obiettivo è verificare l'esistenza dell'oggetto s3.
Ashish Goel

@AshishGoel, ci saranno sempre metadati, se l'oggetto esiste. In effetti, la richiesta HTTP sottostante è semplicemente una TESTA dell'URL dell'oggetto.
Paul Draper

5

Usa ListObjectsRequest impostando Prefix come chiave.

Codice .NET:

 public bool Exists(string key)
    {

        using (Amazon.S3.AmazonS3Client client = (Amazon.S3.AmazonS3Client)Amazon.AWSClientFactory.CreateAmazonS3Client(m_accessKey, m_accessSecret))
        {
            ListObjectsRequest request = new ListObjectsRequest();
            request.BucketName = m_bucketName;
            request.Prefix = key;
            using (ListObjectsResponse response = client.ListObjects(request))
            {

                foreach (S3Object o in response.S3Objects)
                {
                    if( o.Key == key )
                        return true;
                }
                return false;
            }
        }
    }.

7
AVVERTIMENTO! Amazon addebita un extra per ogni chiamata LIST! Questo metodo va bene, ma non usarlo per controllare se il file esiste prima di scaricarlo.
user34402

Questo non è un buon modo per capire se un file esiste poiché ottiene tutti gli oggetti che corrispondono al prefisso. Se hai più file che iniziano con la chiave, scaricherà tutti gli oggetti, compreso quello che hai specificato.
Crypth

Per quanto riguarda il costo di LIST vs GET: tieni presente che ti verranno addebitati anche i dati trasferiti. Quindi, se è estremamente improbabile che il file esista (ad esempio, hai generato un UUID casuale come chiave e vuoi assicurarti che non sia già in uso), GET è molto più economico. Ma se i file sono 0,5 MB e hanno già l'11% di possibilità di esistere, LIST sembra un po 'più economico. Lo stesso se i file sono 0,1 MB e hanno una probabilità del 52% di esistere ... Più grandi sono i file, prima LIST diventa più economico. Ma ancora una volta, uno scenario comune sta testando una chiave UUID appena generata e GET è più conveniente per questo.
Bampfer

5

Per PHP (so che la domanda è Java, ma Google mi ha portato qui), puoi utilizzare stream wrapper e file_exists

$bucket = "MyBucket";
$key = "MyKey";
$s3 = Aws\S3\S3Client->factory([...]);
$s3->registerStreamWrapper();
$keyExists = file_exists("s3://$bucket/$key");

4

Questo codice java controlla se la chiave (file) esiste nel bucket s3.

public static boolean isExistS3(String accessKey, String secretKey, String bucketName, String file) {

    // Amazon-s3 credentials
    AWSCredentials myCredentials = new BasicAWSCredentials(accessKey, secretKey); 
    AmazonS3Client s3Client = new AmazonS3Client(myCredentials); 

    ObjectListing objects = s3Client.listObjects(new ListObjectsRequest().withBucketName(bucketName).withPrefix(file));

    for (S3ObjectSummary objectSummary: objects.getObjectSummaries()) {
        if (objectSummary.getKey().equals(file)) {
            return true;
        }
    }
    return false;
}

2
Questo dovrebbe funzionare, ma dovrebbe anche essere lento nel caso in cui ci siano migliaia o file e per ogni ciclo di file sarebbe necessario.
Danijel

come ha detto @Danijel, questo determinerà effettivamente se esiste o meno un oggetto di una determinata chiave, ma per farlo deve eseguire il loop su potenzialmente decine di migliaia di oggetti in S3 prima di determinare se esiste o meno
Don Cheadle

1
Non sono d'accordo con @Danijel e mmcrae sul fatto che sia lento. La richiesta listObjects specifica .withPrefix (file) quindi dovrebbe restituire al massimo il singolo file corrispondente, a meno che non vi siano altri file il cui nome inizia con il nome del file di destinazione.
davidwebster48

3

Spezza il tuo percorso in secchio e oggetto. Testare il bucket utilizzando il metodo doesBucketExist, Testare l'oggetto utilizzando la dimensione dell'elenco (0 nel caso in cui non esista). Quindi questo codice farà:

String bucket = ...;
String objectInBucket = ...;
AmazonS3 s3 = new AmazonS3Client(...);
return s3.doesBucketExist(bucket) 
       && !s3.listObjects(bucket, objectInBucket).getObjectSummaries().isEmpty();

Facile e semplice. Grazie
Thermech

3

Utilizzo di Object isting. Funzione Java per verificare se la chiave specificata esiste in AWS S3.

boolean isExist(String key)
    {
        ObjectListing objects = amazonS3.listObjects(new ListObjectsRequest().withBucketName(bucketName).withPrefix(key));

        for (S3ObjectSummary objectSummary : objects.getObjectSummaries())
        {
            if (objectSummary.getKey().equals(key))
            {
                return true;
            }

        }
        return false;
    }

2

Il modo giusto per farlo in SDK V2, senza il sovraccarico di ottenere effettivamente l'oggetto, è usare S3Client.headObject . Supportato ufficialmente da AWS Change Log .

Codice di esempio:

public boolean exists(String bucket, String key) {
    try {
        HeadObjectResponse headResponse = client
                .headObject(HeadObjectRequest.builder().bucket(bucket).key(key).build());
        return true;
    } catch (NoSuchKeyException e) {
        return false;
    }
}

1

C'è un modo semplice per farlo usando il metodo isObjectInBucket () dell'API jetS3t.

Codice d'esempio:

ProviderCredentials awsCredentials = new AWSCredentials(
                awsaccessKey,
                awsSecretAcessKey);

        // REST implementation of S3Service
        RestS3Service restService = new RestS3Service(awsCredentials);

        // check whether file exists in bucket
        if (restService.isObjectInBucket(bucket, objectKey)) {

            //your logic

        }

Fa la stessa chiamata get-metadata sotto il cofano + eccezione: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…
alexandroid

1

Le altre risposte sono per AWS SDK v1. Ecco un metodo per AWS SDK v2 (attualmente 2.3.9).

Nota che i metodi getObjectMetadatae doesObjectExistnon sono attualmente nell'SDK v2! Quindi quelle non sono più opzioni. Siamo costretti a usare o getObjecto listObjects.

listObjectsle chiamate sono attualmente 12,5 volte più costose da effettuare rispetto a getObject. Ma AWS addebita anche i dati scaricati, il che aumenta il prezzo getObject se il file esiste . Finché è molto improbabile che il file esista (ad esempio, hai generato una nuova chiave UUID in modo casuale e devi solo ricontrollare che non sia stata utilizzata), la chiamata getObjectè significativamente più economica secondo il mio calcolo.

Solo per essere sicuro, però, ho aggiunto una range()specifica per chiedere ad AWS di inviare solo pochi byte del file. Per quanto ne so, l'SDK lo rispetterà sempre e non ti addebiterà il download dell'intero file. Ma non l'ho verificato, quindi affidati a quel comportamento a tuo rischio! (Inoltre, non sono sicuro di come rangesi comporti se l'oggetto S3 è lungo 0 byte.)

    private boolean sanityCheckNewS3Key(String bucket, String key) {

        ResponseInputStream<GetObjectResponse> resp = null;
        try {
            resp = s3client.getObject(GetObjectRequest.builder()
                .bucket(bucket)
                .key(key)
                .range("bytes=0-3")
                .build());
        }
        catch (NoSuchKeyException e) {
            return false;
        }
        catch (AwsServiceException se) {
            throw se;
        }
        finally {
            if (resp != null) {
                try {
                    resp.close();
                } catch (IOException e) {
                    log.warn("Exception while attempting to close S3 input stream", e);
                }
            }
        }
        return true;
    }
}

Nota: questo codice presuppone s3Cliente logviene dichiarato e inizializzato altrove. Il metodo restituisce un valore booleano, ma può generare eccezioni.


Sembra che ora ci sia un s3Client.headObject()V2 per farlo: stackoverflow.com/a/56949742/9814131 , e controllerai il S3Exceptioncodice di stato 404 per verificare se l'oggetto esiste in base al problema di github github.com/aws/aws-sdk- java-v2 / issues / 297 . Ma immagino che i tuoi siano più progressivi poiché ha un sovraccarico minimo di 0-3 byte.
Shaung Cheng

1

Ho anche affrontato questo problema quando l'ho usato

String BaseFolder = "3patti_Logs"; 
S3Object object = s3client.getObject(bucketName, BaseFolder);
 

Ho ricevuto la chiave di errore non trovata

Quando colpisco e provo

String BaseFolder = "3patti_Logs"; 
S3Object object = s3client.getObject(bucketName, BaseFolder+"/");

ha funzionato, questo codice funziona con 1.9 jar altrimenti aggiorna a 1.11 e usa doesObjectExist come detto sopra


1

Come altri hanno già detto, per AWS S3 Java SDK 2.10+ puoi utilizzare l' oggetto HeadObjectRequest per verificare se è presente un file nel tuo bucket S3. Questo agirà come una richiesta GET senza effettivamente ottenere il file.

Codice di esempio poiché altri non hanno effettivamente aggiunto alcun codice sopra:

public boolean existsOnS3 () throws Exception {
    try {
       S3Client s3Client = S3Client.builder ().credentialsProvider (...).build ();
       HeadObjectRequest headObjectRequest = HeadObjectRequest.builder ().bucket ("my-bucket").key ("key/to/file/house.pdf").build ();
       HeadObjectResponse headObjectResponse = s3Client.headObject (headObjectRequest);
       return headObjectResponse.sdkHttpResponse ().isSuccessful ();    
   }
   catch (NoSuchKeyException e) {
      //Log exception for debugging
      return false;
   }
}

lancia NoSuchKeyException
Andrii Karaivanskyi

Questo perché la chiave non esiste. Questo è esattamente quello che stai cercando. Quindi gestisci quell'eccezione e restituisci false per essa. Ho aggiornato il codice sopra per includere il try / catch.
Navigatron

Allora non ne hai affatto bisogno headObjectResponse. throws Exceptionnon è necessario.
Andrii Karaivanskyi

@AndriiKaraivanskyi è solo un esempio, non l'ho testato.
Navigatron

headObjectResponse.sdkHttpResponse () .isSuccessful (); ha sempre successo se il file esiste o no?
segna il

0

In alternativa puoi utilizzare la libreria client Minio-Java , la sua Open Source e compatibile con l'API AWS S3.

Puoi usare gli esempi Minio-Java StatObject.java per lo stesso.

import io.minio.MinioClient;
import io.minio.errors.MinioException;

import java.io.InputStream;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;

import org.xmlpull.v1.XmlPullParserException;


public class GetObject {
  public static void main (String [] args)
    genera NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
    // Nota: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY e my-bucketname lo sono
    // valori fittizi, sostituiscili con valori originali.
    // Imposta l'endpoint s3, la regione viene calcolata automaticamente
    MinioClient s3Client = new MinioClient ("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY");
    InputStream stream = s3Client.getObject ("my-bucketname", "my-objectname");

    byte [] buf = nuovo byte [16384];
    int bytesRead;
    while ((bytesRead = stream.read (buf, 0, buf.length))> = 0) {
      System.out.println (new String (buf, 0, bytesRead));
    }

    stream.close ();
  }
}

Spero possa essere d'aiuto.

Disclaimer: lavoro per Minio

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.