Jak sprawdzić, czy określony klucz istnieje w danym zasobniku S3 przy użyciu języka Java

88

Chciałbym sprawdzić, czy klucz istnieje w danym zasobniku za pomocą Javy. Przyjrzałem się API, ale nie ma żadnych przydatnych metod. Próbowałem użyć, getObjectale rzuciło to wyjątek.

w Jego krokach
źródło
2
W przyszłości podaj więcej informacji, na przykład jaki był wyjątek, który dostałeś.
Udzieliłem
4
FYI: W przypadku tego pytania przyjęta odpowiedź nie jest najlepszą odpowiedzią.
malana

Odpowiedzi:

3

Skorzystaj z biblioteki jets3t. Jest o wiele prostszy i bardziej wytrzymały niż zestaw SDK AWS. Korzystając z tej biblioteki, możesz wywołać s3service.getObjectDetails (). Spowoduje to sprawdzenie i pobranie tylko szczegółów obiektu (nie zawartości) obiektu. Jeśli brakuje obiektu, wyrzuci 404. Możesz więc złapać ten wyjątek i zająć się nim w swojej aplikacji.

Ale aby to zadziałało, musisz mieć dostęp do ListBucket dla użytkownika w tym zasobniku. Sam dostęp do GetObject nie zadziała. Powodem jest to, że Amazon uniemożliwi sprawdzenie obecności klucza, jeśli nie masz dostępu do ListBucket. W niektórych przypadkach wystarczy wiedzieć, czy klucz jest obecny, czy nie. Dlatego jeśli nie mają dostępu do ListBucket, nie będą mogli tego zrobić.

sethu
źródło
4
Wszystko - zobacz zaktualizowaną odpowiedź na to pytanie poniżej: stackoverflow.com/a/36653034/49678
alexandroid
3
jets3t to stara, przestarzała biblioteka. Zamiast tego użyj aws-java-sdk.
the_storyteller
„łatwiejsze i solidniejsze” jest bardzo subiektywne
Leo Romanovsky
296

W oficjalnym interfejsie API języka Java jest teraz metoda doObjectExist .

Cieszyć się!

malana
źródło
13
Został dodany w 1.10.51
parowiec25
5
Musimy to zagłosować i przenieść to na szczyt!
SureshS
2
Właściwe byłoby uczynienie tej odpowiedzi akceptowaną, ale tylko PO może to zrobić. meta.stackexchange.com/questions/120568/…
malana
4
To musi wykonać połączenie sieciowe, co jest kosztowne, jeśli masz dużo obiektów ... Szkoda, że ​​nie może po prostu zwrócić wartości null w żądaniu metadanych.
Joel,
9
Wygląda na to, że Amazon został usunięty doesObjectExistz 2.x SDK (obecnie v2.3.9).
Bampfer
59

Aktualizacja:

Wygląda na to, że istnieje nowe API, które to sprawdza. Zobacz inną odpowiedź na tej stronie: https://stackoverflow.com/a/36653034/435605

Oryginalny post:

Posługiwać się 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);
}

Uwaga na temat wyjątku: wiem, że wyjątków nie należy używać do sterowania przepływem. Problem polega na tym, że Amazon nie dostarczył żadnego interfejsu API do sprawdzenia tego przepływu - tylko dokumentacja dotycząca wyjątku.

AlikElzin-kilaka
źródło
14
Nie używaj obsługi wyjątków do sterowania programem.
Simon Peck
34
@SimonPeck: masz rację. Problem polega na tym, że Amazon nie dostarczył żadnego interfejsu API do sprawdzenia tego przepływu - tylko dokumentacja dotycząca wyjątku. Usuń swój głos przeciw, jeśli nie głosujesz za nim.
AlikElzin-kilaka
1
Wydaje się, że nie jest to już prawdą w przypadku zestawu Java SDK. Widzę, że mój errorMessagejest ustawiony na „Nie znaleziono”, ale errorCodejest pusty.
bstempi
3
Poszukałbym kodu statusu 404. Wydaje się bardziej wytrzymały niż patrzenie na ciąg
Oskar Kjellin
2
Komentarz @rboarman jest niepoprawny - tak NoSuchKey. Ostateczną listę kodów błędów S3 można znaleźć w dokumentacji: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George,
23

Korzystając z AWS SDK, użyj metody getObjectMetadata. Metoda zgłosi wyjątek AmazonServiceException, jeśli klucz nie istnieje.

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
źródło
2
getObject zgłasza również wyjątek AmazonServiceException, więc po co dwa wywołania? Skąd mam wiedzieć, że obiekt nie istnieje z tego wyjątku? Być może było to spowodowane innym błędem S3 i obiekt rzeczywiście został znaleziony.
AlikElzin-kilaka
5
Nie używaj obsługi wyjątków do sterowania programem.
Simon Peck
4
@ AlikElzin-kilaka, ponieważ getObject () oznacza, że ​​musisz pobrać zawartość obiektu, który może być ogromny.
Jason Nichols
18
@SimonPeck, nie jest to idealne rozwiązanie, ale kiedy Amazon oferuje odpowiednią metodę exist (), to twój punkt widzenia jest ważny.
Jason Nichols
4
@SimonPeck czy masz w tym przypadku alternatywę? To nie jest rażące nadużycie wyjątków jako przepływu kontroli programu ... to jest proste, dokładne w tym, co robi i bezpieczne. Jeśli doprowadzisz swój pomysł do skrajności (tak jak najwyraźniej jesteś, jeśli uważasz, że ten fragment kodu nadużywa wyjątków), to po co w ogóle mieć wyjątki w języku? Zamiast zgłaszać wyjątek, aby zaalarmować program i zmienić jego przepływ , środowisko wykonawcze powinno po prostu zakończyć.
Don Cheadle
17

W Amazon Java SDK 1.10+ możesz użyć, getStatusCode()aby uzyskać kod stanu odpowiedzi HTTP, który będzie 404, jeśli obiekt nie istnieje.

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()zużywa mniej zasobów, a odpowiedź nie musi być zamykana jak getObject().


W poprzednich wersjach można użyć getErrorCode()i sprawdzić odpowiedni ciąg (w zależności od wersji).

Paul Draper
źródło
Jeśli do obiektu s3 nie są dołączone żadne metadane, metoda getObjectMetadata zgłosi błąd 404, nawet jeśli obiekt s3 istnieje. Nie polecam tego, jeśli celem jest sprawdzenie istnienia obiektu s3.
Ashish Goel,
@AshishGoel, zawsze będą metadane, jeśli obiekt istnieje. W rzeczywistości bazowe żądanie HTTP to po prostu HEAD do adresu URL obiektu.
Paul Draper
5

Użyj Prefiksu ustawienia ListObjectsRequest jako klucza.

Kod .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;
            }
        }
    }.
user34402
źródło
7
OSTRZEŻENIE! Amazon pobiera dodatkową opłatę za każde połączenie z LISTĄ! Ta metoda jest w porządku, ale nie używaj jej do sprawdzania, czy plik istnieje przed jego pobraniem.
user34402
Nie jest to dobry sposób na sprawdzenie, czy plik istnieje, ponieważ pobiera wszystkie obiekty zgodne z prefiksem. Jeśli masz wiele plików zaczynających się od klucza, pobierze wszystkie obiekty, w tym określony przez Ciebie.
Crypth
Jeśli chodzi o koszt LIST vs GET: pamiętaj, że naliczana jest również opłata za wszelkie przesłane dane. Więc jeśli jest bardzo mało prawdopodobne, że plik istnieje (na przykład wygenerowałeś losowy UUID jako klucz i chcesz się upewnić, że nie jest już używany), GET jest znacznie tańszy. Ale jeśli pliki mają 0,5 MB i mają 11% szans na istnienie już, wówczas LISTA wygląda trochę taniej. To samo, jeśli pliki mają 0,1 MB i mają 52% szans na istnienie ... Im większe pliki, tym szybciej LISTA staje się tańsza. Ale znowu, typowym scenariuszem jest testowanie nowo wygenerowanego klucza UUID, a GET jest do tego tańszy.
Bampfer
5

W przypadku PHP (wiem, że pytanie brzmi Java, ale Google mnie tu przywiodło) możesz użyć opakowań strumieniowych i file_exists

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

Ten kod java sprawdza, czy klucz (plik) istnieje w zasobniku 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;
}
c0mrade
źródło
2
Powinno to działać, ale powinno również działać wolno w przypadku tysięcy plików i dla każdej pętli plików byłaby potrzebna.
Danijel
jak powiedział @Danijel, to rzeczywiście określi, czy obiekt danego klucza istnieje, ale aby to zrobić, musi zapętlić potencjalnie dziesiątki tysięcy obiektów w S3 przed ustaleniem, czy istnieje, czy nie
Don Cheadle
1
Nie zgadzam się z @Danijel i mmcrae co do tego, że jest to powolne. Żądanie listObjects określa .withPrefix (plik), więc powinno zwrócić co najwyżej jeden pasujący plik, chyba że istnieją inne pliki, których nazwa zaczyna się od nazwy pliku docelowego.
davidwebster48
3

Przebij swoją ścieżkę do wiadra i przedmiotu. Testowanie zasobnika za pomocą metody doesBucketExist, Testowanie obiektu przy użyciu rozmiaru listy (0 w przypadku braku). Więc ten kod zrobi:

String bucket = ...;
String objectInBucket = ...;
AmazonS3 s3 = new AmazonS3Client(...);
return s3.doesBucketExist(bucket) 
       && !s3.listObjects(bucket, objectInBucket).getObjectSummaries().isEmpty();
roee
źródło
Łatwe i proste. Dzięki
Thermech,
3

Korzystanie z Object isting. Funkcja Java do sprawdzania, czy określony klucz istnieje w 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;
    }
Kaustuv
źródło
2

Właściwym sposobem na zrobienie tego w SDK V2, bez przeciążania faktycznego pobierania obiektu, jest użycie S3Client.headObject . Oficjalnie wspierane przez AWS Change Log .

Przykładowy kod:

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;
    }
}
ET
źródło
1

Można to łatwo zrobić za pomocą metody isObjectInBucket () interfejsu API jetS3t.

Przykładowy kod:

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
źródło
Wykonuje to samo wywołanie get-metadata pod maską + wyjątek: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/ ...
alexandroid
1

Pozostałe odpowiedzi dotyczą AWS SDK v1. Oto metoda dla AWS SDK v2 (obecnie 2.3.9).

Zwróć uwagę, że metody getObjectMetadatai doesObjectExistnie są obecnie dostępne w zestawie SDK w wersji 2! Więc to nie są już opcje. Jesteśmy zmuszeni użyć albo getObjectlublistObjects .

listObjectspołączenia są obecnie 12,5 razy droższe niż getObject. Ale AWS pobiera również opłaty za pobrane dane, co podnosi cenę, getObject jeśli plik istnieje . O ile jest bardzo mało prawdopodobne, że plik istnieje (na przykład wygenerowałeś losowo nowy klucz UUID i po prostu musisz dwukrotnie sprawdzić, czy nie jest zajęty), a następnie wywołaniegetObject według moich obliczeń jest znacznie tańsze.

Jednak range()na wszelki wypadek dodałem specyfikację, aby poprosić AWS o przesłanie tylko kilku bajtów pliku. O ile wiem, SDK zawsze to uszanuje i nie pobierze opłat za pobranie całego pliku. Ale nie zweryfikowałem tego, więc polegaj na tym zachowaniu na własne ryzyko! (Nie jestem też pewien, jak się rangezachowa, jeśli obiekt S3 ma długość 0 bajtów).

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

Uwaga: ten kod zakłada s3Clienti logjest zadeklarowany i zainicjowany w innym miejscu. Metoda zwraca wartość logiczną, ale może zgłaszać wyjątki.

Bampfer
źródło
Wygląda na to, że teraz jest s3Client.headObject()w V2 do zrobienia tego: stackoverflow.com/a/56949742/9814131 , a sprawdzisz S3Exceptionkod stanu 404, aby sprawdzić, czy obiekt istnieje zgodnie z problemem github github.com/aws/aws-sdk- java-v2 / Issues / 297 . Ale wydaje mi się, że twój jest bardziej progresywny, ponieważ ma bardzo mały narzut, jak 0-3 bajtów.
Shaung Cheng
1

Miałem też ten problem, kiedy korzystałem

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

Nie znaleziono klucza błędu

Kiedy uderzam i próbuję

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

zadziałało, ten kod działa z jar 1.9, w przeciwnym razie zaktualizuj do wersji 1.11 i użyj doObjectExist, jak wspomniano powyżej

Aakash Sharma
źródło
1

Jak wspominali inni, w przypadku AWS S3 Java SDK 2.10+ możesz użyć HeadObjectRequest obiektu aby sprawdzić, czy S3 znajduje się plik. Będzie to działać jak żądanie GET bez faktycznego pobierania pliku.

Przykładowy kod, ponieważ inni faktycznie nie dodali żadnego kodu powyżej:

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;
   }
}
Navigatron
źródło
rzuca NoSuchKeyException
Andrii Karaivanskyi
To dlatego, że klucz nie istnieje. To jest dokładnie to, czego szukasz. Więc obsłuż ten wyjątek i zwróć dla niego false. Zaktualizowałem powyższy kod, aby zawierał try / catch.
Navigatron
Wtedy wcale nie potrzebujesz headObjectResponse. throws Exceptionnie jest również potrzebne.
Andrii Karaivanskyi
@AndriiKaraivanskyi to tylko przykład, nie testowałem tego.
Navigatron
headObjectResponse.sdkHttpResponse () .isSuccessful (); zawsze kończy się sukcesem, niezależnie od tego, czy plik istnieje, czy nie?
znak
0

Alternatywnie możesz użyć biblioteki klienckiej Minio-Java , jej Open Source i kompatybilnej z API AWS S3.

Możesz użyć przykładów Minio-Java StatObject.java do tego samego.

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)
    rzuca NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
    // Uwaga: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY i my-bucketname to
    // wartości fikcyjne, zastąp je oryginalnymi wartościami.
    // Ustaw punkt końcowy s3, region jest obliczany automatycznie
    MinioClient s3Client = nowy MinioClient („https://s3.amazonaws.com”, „TWÓJ-KLUCZ DOSTĘPU”, „TWÓJ-SEKRETAKCESKLUCZ”);
    InputStream stream = s3Client.getObject ("my-bucketname", "my-objectname");

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

    stream.close ();
  }
}

Mam nadzieję, że to pomoże.

Zastrzeżenie: pracuję dla Minio

koolhead17
źródło