So überprüfen Sie mithilfe von Java, ob ein bestimmter Schlüssel in einem bestimmten S3-Bucket vorhanden ist

86

Ich möchte mit Java überprüfen, ob ein Schlüssel in einem bestimmten Bucket vorhanden ist. Ich habe mir die API angesehen, aber es gibt keine nützlichen Methoden. Ich habe versucht zu verwenden, getObjectaber es warf eine Ausnahme.

in seinen Schritten
quelle
2
In Zukunft geben Sie bitte weitere Informationen an, wie z. B. die Ausnahme, die Sie erhalten haben. Ich habe eine Antwort gegeben, die auf einer Annahme basiert.
Sethu
4
Zu Ihrer Information: Für diese Frage ist die akzeptierte Antwort nicht die beste Antwort.
Malana

Antworten:

3

Verwenden Sie die jets3t-Bibliothek. Es ist viel einfacher und robuster als das AWS SDK. Mit dieser Bibliothek können Sie s3service.getObjectDetails () aufrufen. Dadurch werden nur die Details des Objekts (nicht der Inhalt) des Objekts überprüft und abgerufen. Es wird eine 404 geworfen, wenn das Objekt fehlt. So können Sie diese Ausnahme abfangen und in Ihrer App behandeln.

Damit dies funktioniert, benötigen Sie jedoch ListBucket-Zugriff für den Benutzer in diesem Bucket. Nur GetObject-Zugriff funktioniert nicht. Der Grund dafür ist, dass Amazon Sie daran hindert, das Vorhandensein des Schlüssels zu überprüfen, wenn Sie keinen ListBucket-Zugriff haben. Nur zu wissen, ob ein Schlüssel vorhanden ist oder nicht, reicht in einigen Fällen auch für böswillige Benutzer aus. Daher können sie dies nicht tun, es sei denn, sie haben ListBucket-Zugriff.

Sethu
quelle
4
Alle - siehe eine aktualisierte Antwort auf diese Frage unten: stackoverflow.com/a/36653034/49678
alexandroid
3
jets3t ist eine alte veraltete Bibliothek. Verwenden Sie stattdessen das aws-java-sdk.
the_storyteller
"einfacher und robuster" ist sehr subjektiv
Leo Romanovsky
287

Es gibt jetzt eine doesObjectExist- Methode in der offiziellen Java-API.

Genießen!

Malana
quelle
13
Es wurde in 1.10.51
Dampfer25
4
Wir müssen dies positiv bewerten und an die Spitze bringen!
SureshS
2
Das Richtige wäre, dies zur akzeptierten Antwort zu machen, aber das kann nur das OP. meta.stackexchange.com/questions/120568/…
Malana
4
Dies muss einen Netzwerkaufruf ausführen, der teuer ist, wenn Sie viele Objekte haben ... Schade, dass bei der Metadatenanforderung nicht einfach null zurückgegeben werden kann.
Joel
9
Es sieht so aus, als ob Amazon doesObjectExistaus dem 2.x SDK entfernt wurde (derzeit v2.3.9).
Bampfer
59

Aktualisieren:

Es scheint, dass es eine neue API gibt, um genau das zu überprüfen. Eine weitere Antwort finden Sie auf dieser Seite: https://stackoverflow.com/a/36653034/435605

Ursprünglicher Beitrag:

Verwenden 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);
}

Hinweis zur Ausnahme: Ich weiß, dass Ausnahmen nicht für die Flusskontrolle verwendet werden sollten. Das Problem ist, dass Amazon keine API zur Überprüfung dieses Ablaufs bereitgestellt hat - nur eine Dokumentation über die Ausnahme.

AlikElzin-Kilaka
quelle
14
Verwenden Sie keine Ausnahmebehandlung für die Programmsteuerung.
Simon Peck
34
@ SimonPeck: Du hast recht. Das Problem ist, dass Amazon keine API zur Überprüfung dieses Ablaufs bereitgestellt hat - nur Dokumentation über die Ausnahme. Bitte entfernen Sie Ihre Abwärtsstimme, wenn Sie sie nicht hochstimmen.
AlikElzin-Kilaka
1
Dies scheint für das Java SDK nicht mehr zuzutreffen. Ich sehe, dass mein errorMessageauf "Nicht gefunden" gesetzt ist, aber das errorCodeist null.
Bstempi
3
Ich würde nach dem Statuscode 404 suchen. Scheint robuster als eine Zeichenfolge zu betrachten
Oskar Kjellin
2
Der Kommentar von @rboarman ist falsch - es ist NoSuchKey. Eine endgültige Liste der S3-Fehlercodes finden Sie in der Dokumentation: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George
22

Verwenden Sie für das AWS SDK die Methode getObjectMetadata. Die Methode löst eine AmazonServiceException aus, wenn der Schlüssel nicht vorhanden ist.

private AmazonS3 s3;
...
public boolean exists(String path, String name) {
    try {
        s3.getObjectMetadata(bucket, getS3Path(path) + name); 
    } catch(AmazonServiceException e) {
        return false;
    }
    return true;
}
user979051
quelle
2
getObject löst auch AmazonServiceException aus. Warum also zwei Aufrufe? Woher weiß ich auch, dass das Objekt aus dieser Ausnahme nicht existiert? Vielleicht lag es an einem anderen S3-Fehler und das Objekt wurde tatsächlich gefunden.
AlikElzin-Kilaka
5
Verwenden Sie keine Ausnahmebehandlung für die Programmsteuerung.
Simon Peck
4
@ AlikElzin-kilaka, weil getObject () bedeutet, dass Sie den Inhalt des Objekts herunterladen müssen, der möglicherweise sehr groß sein kann.
Jason Nichols
18
@SimonPeck, es ist nicht ideal, aber wenn Amazon eine geeignete existierende () Methode anbietet, ist Ihr Punkt gültig.
Jason Nichols
4
@ SimonPeck hast du in diesem Fall eine Alternative? Dies ist kein offensichtlicher Missbrauch von Ausnahmen als Programmsteuerungsablauf ... dies ist einfach, genau und sicher. Wenn Sie Ihre Idee auf die Spitze treiben (wie es anscheinend der Fall ist, wenn Sie glauben, dass dieses Code-Snippet Ausnahmen missbraucht), warum gibt es dann überhaupt Ausnahmen in einer Sprache? Anstatt eine Ausnahme auszulösen, um das Programm zu alarmieren und den Programmablauf zu ändern , sollte die Laufzeit vermutlich einfach beendet werden.
Don Cheadle
16

In Amazon Java SDK 1.10+ können Sie getStatusCode()den Statuscode der HTTP-Antwort abrufen, der 404 lautet, wenn das Objekt nicht vorhanden ist.

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()verbraucht weniger Ressourcen und die Antwort muss nicht wie geschlossen werden getObject().


In früheren Versionen können Sie getErrorCode()die entsprechende Zeichenfolge verwenden und nach ihr suchen (abhängig von der Version).

Paul Draper
quelle
Wenn an Ihr s3-Objekt keine Metadaten angehängt sind, gibt getObjectMetadata einen 404-Fehler aus, selbst wenn das s3-Objekt vorhanden ist. Ich werde dies nicht empfehlen, wenn das Ziel darin besteht, die Existenz des s3-Objekts zu überprüfen.
Ashish Goel
@AshishGoel, es gibt immer Metadaten, wenn das Objekt existiert. Tatsächlich ist die zugrunde liegende HTTP-Anforderung einfach ein HEAD zur URL des Objekts.
Paul Draper
5

Verwenden Sie das ListObjectsRequest-Einstellungspräfix als Schlüssel.

.NET-Code:

 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;
            }
        }
    }.
user34402
quelle
7
WARNUNG! Amazon berechnet für jeden LIST-Anruf einen Aufpreis! Diese Methode ist in Ordnung, aber verwenden Sie sie nicht, um zu überprüfen, ob eine Datei vorhanden ist, bevor Sie sie herunterladen.
user34402
Dies ist kein guter Weg, um festzustellen, ob eine Datei vorhanden ist, da alle Objekte abgerufen werden, die dem Präfix entsprechen. Wenn Sie mehrere Dateien haben, die mit dem Schlüssel beginnen, werden alle Objekte heruntergeladen, einschließlich des von Ihnen angegebenen.
Crypth
In Bezug auf die Kosten von LIST vs GET: Beachten Sie, dass Ihnen auch alle übertragenen Daten in Rechnung gestellt werden. Wenn es also äußerst unwahrscheinlich ist, dass die Datei vorhanden ist (Sie haben beispielsweise eine zufällige UUID als Schlüssel generiert und möchten sicherstellen, dass sie nicht bereits verwendet wird), ist GET viel billiger. Wenn die Dateien jedoch 0,5 MB groß sind und eine Wahrscheinlichkeit von 11% besteht, dass sie bereits vorhanden sind, sieht LIST etwas billiger aus. Gleiches gilt, wenn die Dateien 0,1 MB groß sind und eine 52% ige Chance haben, vorhanden zu sein ... Je größer die Dateien, desto schneller wird die LISTE billiger. Ein häufiges Szenario ist jedoch das Testen eines neu generierten UUID-Schlüssels, und dafür ist GET billiger.
Bampfer
5

Für PHP (ich weiß, dass die Frage Java ist, aber Google hat mich hierher gebracht) können Sie Stream-Wrapper und file_exists verwenden

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

Dieser Java-Code prüft, ob der Schlüssel (die Datei) im s3-Bucket vorhanden ist.

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;
}
c0mrade
quelle
2
Dies sollte funktionieren, sollte aber auch langsam sein, wenn es Tausende oder Dateien gibt, und für jede Dateischleife wäre erforderlich.
Danijel
Wie @Danijel sagte, wird dies tatsächlich bestimmen, ob ein Objekt eines bestimmten Schlüssels existiert oder nicht, aber um dies zu tun, muss es möglicherweise Zehntausende von Objekten in S3 durchlaufen, bevor bestimmt wird, ob es existiert oder nicht
Don Cheadle
1
Ich bin mit @Danijel und mmcrae nicht einverstanden, dass dies langsam ist. Die Anforderung listObjects gibt .withPrefix (Datei) an, sodass höchstens die einzelne übereinstimmende Datei zurückgegeben werden soll, es sei denn, es gibt andere Dateien, deren Name mit dem Namen der Zieldatei beginnt.
Davidwebster48
3

Brechen Sie Ihren Weg in Eimer und Objekt. Testen des Buckets mit der Methode doesBucketExist, Testen des Objekts mit der Größe der Auflistung (0, falls nicht vorhanden). Dieser Code reicht also aus:

String bucket = ...;
String objectInBucket = ...;
AmazonS3 s3 = new AmazonS3Client(...);
return s3.doesBucketExist(bucket) 
       && !s3.listObjects(bucket, objectInBucket).getObjectSummaries().isEmpty();
Roee
quelle
Leicht und einfach. Danke
Thermech
3

Verwenden von Objectisting. Java-Funktion zum Überprüfen, ob der angegebene Schlüssel in AWS S3 vorhanden ist.

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;
    }
Kaustuv
quelle
1

Es gibt eine einfache Möglichkeit, dies mit der isObjectInBucket () -Methode der jetS3t-API zu tun.

Beispielcode:

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

        }
Dhwaneel
quelle
Es macht den gleichen Get-Metadaten-Aufruf unter der Haube + Ausnahmefang: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…
alexandroid
1

Die anderen Antworten beziehen sich auf AWS SDK v1. Hier ist eine Methode für AWS SDK v2 (derzeit 2.3.9).

Beachten Sie, dass getObjectMetadataund doesObjectExistMethoden derzeit nicht im v2 SDK enthalten sind! Das sind also keine Optionen mehr. Wir sind gezwungen, entweder getObjectoder zu verwenden listObjects.

listObjectsAnrufe sind derzeit 12,5-mal teurer als getObject. AWS berechnet jedoch auch Gebühren für heruntergeladene Daten, was den Preis erhöht, getObject wenn die Datei vorhanden ist . Solange es sehr unwahrscheinlich ist, dass die Datei existiert (Sie haben beispielsweise zufällig einen neuen UUID-Schlüssel generiert und müssen nur überprüfen, ob er nicht verwendet wird), getObjectist das Aufrufen nach meiner Berechnung erheblich billiger.

Um auf der sicheren Seite zu sein, habe ich eine range()Spezifikation hinzugefügt , um AWS zu bitten, nur einige Bytes der Datei zu senden. Soweit ich weiß, wird das SDK dies immer respektieren und Ihnen keine Gebühren für das Herunterladen der gesamten Datei berechnen. Aber ich habe das nicht überprüft, also verlasse dich auf dieses Verhalten auf eigenes Risiko! (Ich bin mir auch nicht sicher, wie ich mich rangeverhalte, wenn das S3-Objekt 0 Byte lang ist.)

    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;
    }
}

Hinweis: Dieser Code geht davon aus s3Clientund logwird an anderer Stelle deklariert und initialisiert. Die Methode gibt einen Booleschen Wert zurück, kann jedoch Ausnahmen auslösen.

Bampfer
quelle
Anscheinend gibt es jetzt s3Client.headObject()in V2 eine Möglichkeit , dies zu tun: stackoverflow.com/a/56949742/9814131 , und Sie überprüfen den S3ExceptionStatuscode 404 des S, um zu überprüfen, ob das Objekt gemäß dem Github-Problem github.com/aws/aws-sdk- vorhanden ist. Java-v2 / Issues / 297 . Aber ich denke, Ihre sind progressiver, da sie nur einen geringen Overhead von 0-3 Bytes haben.
Shaung Cheng
0

Alternativ können Sie die Minio-Java- Clientbibliothek Open Source verwenden, die mit der AWS S3-API kompatibel ist.

Sie können dafür Minio-Java StatObject.java- Beispiele verwenden.

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;


öffentliche Klasse GetObject {
  public static void main (String [] args)
    löst NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {aus
    // Hinweis: IHR ACCESSKEYID, IHR SECRETACCESSKEY und mein Bucketname sind
    // Dummy-Werte, bitte durch Originalwerte ersetzen.
    // s3 Endpunkt setzen, Region wird automatisch berechnet
    MinioClient s3Client = neuer MinioClient ("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY");
    InputStream stream = s3Client.getObject ("mein-Bucket-Name", "mein-Objektname");

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

    stream.close ();
  }}
}}

Ich hoffe, es hilft.

Haftungsausschluss: Ich arbeite für Minio

koolhead17
quelle
0

Wie bereits erwähnt, können Sie für das AWS S3 Java SDK 2.10+ das HeadObjectRequest- Objekt verwenden, um zu überprüfen, ob sich eine Datei in Ihrem S3-Bucket befindet. Dies verhält sich wie eine GET-Anforderung, ohne die Datei tatsächlich abzurufen.

Beispielcode, da andere oben keinen Code hinzugefügt haben:

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 (NoSuckKeyException e) {
      //Log exception for debugging
      return false;
   }
}
Navigatron
quelle
wirft NoSuchKeyException
Andrii Karaivanskyi
Das liegt daran, dass der Schlüssel nicht existiert. Genau das suchen Sie. Behandeln Sie diese Ausnahme und geben Sie false zurück. Ich habe den obigen Code aktualisiert, um den Versuch / Fang einzuschließen.
Navigatron
Dann brauchst du gar nicht headObjectResponse. throws Exceptionwird auch nicht benötigt.
Andrii Karaivanskyi
@AndriiKaraivanskyi es ist nur ein Beispiel, ich habe es nicht getestet.
Navigatron
0

Ich war auch mit diesem Problem konfrontiert, als ich es benutzte

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

Ich habe den Fehlerschlüssel nicht gefunden

Wenn ich treffe und es versuche

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

es hat funktioniert, dieser Code funktioniert mit 1.9 jar, andernfalls aktualisieren Sie auf 1.11 und verwenden Sie doesObjectExist wie oben beschrieben

Aakash Sharma
quelle