Java kullanılarak belirli bir S3 klasöründe belirli bir anahtarın olup olmadığı nasıl kontrol edilir


88

Java kullanan belirli bir pakette bir anahtar olup olmadığını kontrol etmek istiyorum. API'ye baktım ama faydalı olan herhangi bir yöntem yok. Kullanmaya çalıştım getObjectama bir istisna yarattı.


2
Gelecekte, lütfen istisnanızın ne olduğu gibi daha fazla bilgi verin .. Bir varsayıma dayalı bir cevap
verdim

4
Bilginize: Bu soru için kabul edilen cevap en iyi cevap değil.
malana

Yanıtlar:


3

Jets3t kitaplığını kullanın. AWS sdk'den çok daha kolay ve sağlamdır. Bu kitaplığı kullanarak s3service.getObjectDetails () öğesini çağırabilirsiniz. Bu, yalnızca nesnenin ayrıntılarını (içeriğini değil) kontrol edecek ve alacaktır. Nesne eksikse 404 atar. Böylece bu istisnayı yakalayabilir ve uygulamanızda bununla başa çıkabilirsiniz.

Ancak bunun çalışması için, o paketteki kullanıcı için ListBucket erişimine sahip olmanız gerekir. Sadece GetObject erişimi çalışmayacak. Bunun nedeni, ListBucket erişiminiz yoksa Amazon, anahtarın varlığını kontrol etmenizi engelleyecektir. Sadece bir anahtarın var olup olmadığını bilmek, bazı durumlarda kötü niyetli kullanıcılar için de yeterli olacaktır. Bu nedenle, ListBucket erişimine sahip olmadıkça, bunu yapamazlar.


4
Hepsi - aşağıda bu sorunun güncellenmiş cevabına bakın: stackoverflow.com/a/36653034/49678
alexandroid

3
jets3t, kullanımdan kaldırılmış eski bir kitaplıktır. Bunun yerine aws-java-sdk'yi kullanın.
the_storyteller

"daha kolay ve daha sağlam" çok özneldir
Leo Romanovsky

296

Artık resmi Java API'sinde bir doObjectExist yöntemi var.

Zevk almak!


13
1.10.51'de eklendi
steamer25

5
Buna olumlu oy vermeli ve bunu en üste çıkarmalıyız!
SureshS

2
Yapılacak doğru şey, bunu kabul edilen cevap yapmaktır, ancak bunu sadece OP yapabilir. meta.stackexchange.com/questions/120568/…
malana

4
Bu, çok sayıda nesneniz varsa pahalı olan bir ağ araması yapmalıdır ... Çok kötü, meta veri isteğinde yalnızca boş değer döndüremez.
Joel

9
Görünüşe göre Amazon doesObjectExist, 2.x SDK'dan (şu anda v2.3.9) kaldırılmış .
Bampfer

59

Güncelleme:

Görünüşe göre tam da bunu kontrol etmek için yeni bir API var. Bu sayfada başka bir cevaba bakın: https://stackoverflow.com/a/36653034/435605

Orijinal gönderi:

Kullanım 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);
}

İstisna hakkında not: İstisnaların akış kontrolü için kullanılmaması gerektiğini biliyorum. Sorun şu ki, Amazon bu akışı kontrol etmek için herhangi bir API sağlamadı - sadece istisna hakkında belgeler.


14
Program kontrolü için istisna işleme kullanmayın.
Simon Peck

34
@SimonPeck: haklısın. Sorun şu ki, Amazon bu akışı kontrol etmek için herhangi bir API sağlamadı - sadece istisna ile ilgili belgeler. Olumlu oy kullanmıyorsanız lütfen olumsuz oyunuzu kaldırın.
AlikElzin-kilaka

1
Bu artık Java SDK için doğru görünmüyor. errorMessage"Bulunamadı" olarak ayarlandığını görüyorum , ancak errorCodenull.
bstempi

3
404 durum kodunu aramaya giderdim. Bir dizeye bakmaktan daha sağlam görünüyor
Oskar Kjellin

2
@Rboarman tarafından yapılan yorum yanlış - öyle NoSuchKey. S3 hata kodlarının kesin listesi için dokümantasyona bakın: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George

23

AWS SDK'yı kullanarak getObjectMetadata yöntemini kullanın. Anahtar yoksa, yöntem bir AmazonServiceException oluşturur.

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, AmazonServiceException'ı da atar, öyleyse neden iki çağrı yapar? Ayrıca, nesnenin bu uyarılmadan var olmadığını nasıl bilebilirim? Belki de başka bir S3 hatası nedeniyledir ve nesne gerçekten bulunur.
AlikElzin-kilaka

5
Program kontrolü için istisna işleme kullanmayın.
Simon Peck

4
@ AlikElzin-kilaka, çünkü getObject (), potansiyel olarak çok büyük olabilecek nesnenin içeriğini indirmeniz gerektiği anlamına gelir.
Jason Nichols

18
@SimonPeck, ideal değil, ancak Amazon uygun bir exist () yöntemi sunduğunda, puanınız geçerlidir.
Jason Nichols

4
@SimonPeck bu durumda bir alternatifiniz var mı? Bu, program kontrol akışı olarak istisnaların açık bir şekilde kötüye kullanılması değildir ... bu basittir, yaptığı işte doğru ve güvenlidir. Fikrinizi aşırıya götürürseniz (görünüşe göre bu kod parçacığının istisnaları kötüye kullandığını düşünüyorsanız öyledir), o zaman neden bir dilde istisnalar var? Programı uyarmak ve programın akışını değiştirmek için bir istisna atmak yerine , sanırım çalışma zamanı sona ermelidir.
Don Cheadle

17

Amazon Java SDK 1.10+ içinde, getStatusCode()nesne yoksa 404 olacak olan HTTP yanıtının durum kodunu almak için kullanabilirsiniz .

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()daha az kaynak tüketir ve yanıtın kapatılmasına gerek yoktur getObject().


Önceki sürümlerde, getErrorCode()uygun dizeyi kullanabilir ve kontrol edebilirsiniz (sürüme bağlıdır).


S3 nesnenize herhangi bir meta veri eklenmemişse, getObjectMetadata s3 nesnesi mevcut olsa bile bir 404 hatası verir. Amaç s3 nesnesinin varlığını kontrol etmekse bunu önermeyeceğim.
Ashish Goel

@AshishGoel, nesne varsa her zaman meta veriler olacaktır. Aslında, temeldeki HTTP isteği, nesnenin URL'sinin bir HEAD'idir.
Paul Draper

5

Anahtarınız olarak ListObjectsRequest ayarı Önekini kullanın.

.NET kodu:

 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
UYARI! Amazon, her LIST araması için ekstra ücret alır! Bu yöntem tamam, ancak indirmeden önce dosyanın var olup olmadığını kontrol etmek için kullanmayın.
user34402

Bu, önekle eşleşen tüm nesneleri aldığı için bir dosya olup olmadığını anlamanın iyi bir yolu değildir. Anahtarla başlayan birden çok dosyanız varsa, belirttiğiniz dahil tüm nesneleri indirir.
Crypth

LIST ve GET'in maliyeti ile ilgili olarak: aktarılan tüm veriler için de ücretlendirileceğinizi unutmayın. Dolayısıyla , dosyanın var olma olasılığı son derece düşükse (örneğin, anahtar olarak rastgele bir UUID oluşturduysanız ve halihazırda kullanılmadığından emin olmak istiyorsanız) GET çok daha ucuzdur. Ancak dosyalar 0,5 MB ise ve zaten var olma şansı% 11 ise, LIST biraz daha ucuz görünüyor. Dosyalar 0,1 MB büyüklüğünde ise ve% 52 var olma şansı varsa aynıdır ... Dosyalar ne kadar büyükse, LİSTE o kadar ucuz olur. Ancak yine, yaygın bir senaryo, yeni oluşturulan bir UUID anahtarını test etmektir ve GET bunun için daha ucuzdur.
Bampfer

5

PHP için (Sorunun Java olduğunu biliyorum, ancak Google beni buraya getirdi), akış sarmalayıcıları ve file_exists'i kullanabilirsiniz

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

4

Bu java kodu, anahtarın (dosyanın) s3 paketinde mevcut olup olmadığını kontrol eder.

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
Bu işe yaramalı, ancak binlerce dosya veya dosya olduğu durumlarda yavaş olmalı ve her dosya döngüsü için gerekli olacaktır.
Danijel

@Danijel'in dediği gibi, bu gerçekten belirli bir anahtarın bir nesnesinin var olup olmadığını belirleyecektir, ancak bunu yapmak için, var olup olmadığını belirlemeden önce S3'te potansiyel olarak on binlerce nesnenin üzerinden geçmesi gerekir
Don Cheadle

1
@Danijel ve mmcrae'ye bunun yavaş olduğu konusunda katılmıyorum. ListObjects isteği .withPrefix (dosya) belirtir, bu nedenle adı hedef dosyanın adıyla başlayan başka dosyalar olmadığı sürece en fazla tek eşleşen dosyayı döndürmelidir.
davidwebster48

3

Yolunuzu kova ve nesneye ayırın. Yöntemi kullanarak kovayı doesBucketExisttest etme, Listenin boyutunu kullanarak nesneyi test etme (yoksa 0). Yani bu kod şunları yapacak:

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

Kolay ve basit. Teşekkürler
Thermech

3

Object isting'i kullanma. AWS S3'te belirtilen anahtarın mevcut olup olmadığını kontrol etmek için Java işlevi.

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

Bunu SDK V2'de, nesneyi gerçekten alma aşırı yüklemesi olmadan yapmanın doğru yolu, S3Client.headObject'i kullanmaktır . Resmi olarak AWS Değişiklik Günlüğü ile desteklenir .

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

1

JetS3t API'sinin isObjectInBucket () yöntemini kullanarak bunu yapmanın kolay bir yolu vardır.

Basit 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

        }

Başlık + istisna yakalama altında aynı get-metadata çağrısını yapar: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…
alexandroid

1

Diğer yanıtlar AWS SDK v1 içindir. İşte AWS SDK v2 (şu anda 2.3.9) için bir yöntem.

Bunun getObjectMetadatave doesObjectExistyöntemlerin şu anda v2 SDK'da olmadığını unutmayın ! Yani bunlar artık seçenekler değil. Ya getObjectda kullanmak zorunda kalıyoruz listObjects.

listObjectsşu anda arama yapmaktan 12,5 kat daha pahalıdır getObject. Ancak AWS, indirilen tüm veriler için de ücret alır ve getObject bu, dosyanın mevcut olup olmadığının fiyatını yükseltir . Dosyanın var olma olasılığı çok düşük olduğu sürece (örneğin, rastgele yeni bir UUID anahtarı oluşturdunuz ve alınmadığını iki kez kontrol etmeniz gerekiyor), bu durumda arama getObjecthesaplamama göre önemli ölçüde daha ucuz.

Yine de güvenli tarafta olmak için, range()AWS'den dosyanın yalnızca birkaç baytını göndermesini istemek için bir şartname ekledim . Bildiğim kadarıyla, SDK buna her zaman saygı duyacak ve tüm dosyayı indirdiğiniz için sizden ücret talep etmeyecektir. Ama bu davranışa kendi sorumluluğunuzda güvendiğinizi doğrulamadım! (Ayrıca, rangeS3 nesnesi 0 bayt uzunluğundaysa nasıl davranacağından emin değilim .)

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

Not: Bu kod varsayar s3Clientve logbaşka yerlerde ilan ve başlatılır. Yöntem bir boole döndürür, ancak istisnalar atabilir.


Görünüşe göre şimdi s3Client.headObject()V2'de bunu yapacak bir tane var: stackoverflow.com/a/56949742/9814131 ve S3Exceptionnesnenin github sorununa göre var olup olmadığını kontrol etmek için 404 durum kodunu kontrol edeceksiniz github.com/aws/aws-sdk- java-v2 / sorunlar / 297 . Ama sanırım sizinki 0-3 bayt kadar az ek yükü olduğu için daha ilerici.
Shaung Cheng

1

Ben de kullandığımda bu problemle karşılaştım

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

Hata anahtarı bulunamadı

Vurup denediğimde

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

işe yaradı, bu kod 1.9 jar ile çalışıyor, aksi takdirde 1.11'e güncelleyin ve yukarıda belirtildiği gibi doObjectExist'i kullanın


1

Diğerlerinin de belirttiği gibi, AWS S3 Java SDK 2.10+ için, S3 klasörünüzde bir dosya olup olmadığını kontrol etmek için HeadObjectRequest nesnesini kullanabilirsiniz . Bu, dosyayı gerçekten almadan bir GET isteği gibi davranacaktır.

Başkaları aslında yukarıya herhangi bir kod eklemediği için örnek kod:

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

atar NoSuchKeyException
Andrii Karaivanskyi

Bunun nedeni anahtarın olmamasıdır. Tam olarak aradığınız şey bu. Öyleyse bu istisnayı hallet ve bunun için yanlış döndür. Yukarıdaki kodu, try / catch'i içerecek şekilde güncelledim.
Navigatron

O zaman hiç ihtiyacın yok headObjectResponse. throws Exceptionayrıca gerekli değildir.
Andrii Karaivanskyi

@AndriiKaraivanskyi bu sadece bir örnek, test etmedim.
Navigatron

headObjectResponse.sdkHttpResponse () .isSuccessful (); dosya var olsun ya da olmasın her zaman başarılı mıdır?
mark

0

Alternatif olarak Minio-Java kullanabilirsiniz istemci kitaplığını, Açık Kaynağını ve AWS S3 API ile uyumlu kullanabilirsiniz.

Minio-Java StatObject.java'yı kullanabilirsiniz. örneklerini .

io.minio.MinioClient'i içe aktar;
import io.minio.errors.MinioException;

java.io.InputStream'i içe aktarın;
java.io.IOException'ı içe aktarın;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;

import org.xmlpull.v1.XmlPullParserException;


public class GetObject {
  public static void main (String [] değiştirgeler)
    NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
    // Not: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY ve benim-paketim
    // sahte değerler, lütfen bunları orijinal değerlerle değiştirin.
    // s3 uç noktası ayarlayın, bölge otomatik olarak hesaplanır
    MinioClient s3Client = new MinioClient ("https://s3.amazonaws.com", "ERİŞİM ANAHTAR KİMLİĞİNİZ", "SECRETACCESSKEYİNİZ");
    InputStream stream = s3Client.getObject ("paket adım", "nesne adım");

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

    stream.close ();
  }
}

Umut ediyorum bu yardım eder.

Feragatname: Minio için çalışıyorum

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.