Cara memeriksa apakah kunci yang ditentukan ada di bucket S3 tertentu menggunakan Java

88

Saya ingin memeriksa apakah ada kunci dalam bucket tertentu menggunakan Java. Saya melihat API tetapi tidak ada metode yang berguna. Saya mencoba menggunakan getObjecttetapi itu membuat pengecualian.

di Langkah-Nya
sumber
2
Kedepannya mohon berikan lebih banyak informasi seperti apa pengecualian yang anda dapatkan .. Saya telah memberikan jawaban berdasarkan asumsi ..
sethu
4
FYI: Untuk pertanyaan ini, jawaban yang diterima bukanlah jawaban terbaik.
malana

Jawaban:

3

Gunakan perpustakaan jets3t. Ini jauh lebih mudah dan kuat daripada sdk AWS. Menggunakan pustaka ini, Anda bisa memanggil, s3service.getObjectDetails (). Ini akan memeriksa dan mengambil hanya detail objek (bukan konten) objek. Ini akan melempar 404 jika objeknya hilang. Jadi Anda bisa menangkap pengecualian itu dan menanganinya di aplikasi Anda.

Tetapi agar ini berfungsi, Anda harus memiliki akses ListBucket untuk pengguna di keranjang itu. Akses GetObject saja tidak akan bekerja. Alasannya, Amazon akan mencegah Anda memeriksa keberadaan kunci jika Anda tidak memiliki akses ListBucket. Hanya mengetahui apakah kunci ada atau tidak, juga akan cukup untuk pengguna jahat dalam beberapa kasus. Karenanya, kecuali mereka memiliki akses ListBucket, mereka tidak akan dapat melakukannya.

sethu
sumber
4
Semua - lihat jawaban yang diperbarui untuk pertanyaan di bawah ini: stackoverflow.com/a/36653034/49678
alexandroid
3
jets3t adalah pustaka lama yang tidak digunakan lagi. Sebagai gantinya gunakan aws-java-sdk.
the_storyteller
"lebih mudah dan lebih kuat" sangat subjektif
Leo Romanovsky
296

Sekarang ada metode doesObjectExist di API Java resmi.

Nikmati!

malana
sumber
13
Itu ditambahkan pada 1.10.51
steamer25
5
Kami harus memberi suara positif dan membawa ini ke puncak!
SureshS
2
Hal yang benar untuk dilakukan adalah membuat ini menjadi jawaban yang diterima tetapi hanya OP yang dapat melakukannya. meta.stackexchange.com/questions/120568/…
malana
4
Ini harus melakukan panggilan jaringan, yang mahal jika Anda memiliki banyak objek ... Sayang sekali tidak bisa mengembalikan nol pada permintaan metadata.
Joel
9
Sepertinya Amazon dihapus doesObjectExistdari 2.x SDK (saat ini v2.3.9).
Bampfer
59

Memperbarui:

Sepertinya ada API baru untuk memeriksa hal itu. Lihat jawaban lain di halaman ini: https://stackoverflow.com/a/36653034/435605

Posting asli:

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

Catatan tentang pengecualian: Saya tahu pengecualian tidak boleh digunakan untuk kontrol aliran. Masalahnya adalah Amazon tidak menyediakan api apa pun untuk memeriksa aliran ini - hanya dokumentasi tentang pengecualian.

AlikElzin-kilaka
sumber
14
Jangan gunakan penanganan pengecualian untuk kontrol program.
Simon Peck
34
@SimonPeck: Anda benar. Masalahnya adalah Amazon tidak menyediakan api apa pun untuk memeriksa aliran ini - hanya dokumentasi tentang pengecualian. Harap hapus suara negatif Anda jika tidak memberikan suara positif.
AlikElzin-kilaka
1
Ini tampaknya tidak berlaku lagi untuk Java SDK. Saya melihat bahwa my errorMessagedisetel ke "Not Found", tetapi errorCodenull.
bstempi
3
Saya akan mencari kode status 404. Tampaknya lebih kuat daripada melihat string
Oskar Kjellin
2
Komentar oleh @rboarman salah - itu benar NoSuchKey. Untuk daftar definitif kode kesalahan S3, lihat dokumentasi: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George
23

Menggunakan AWS SDK gunakan metode getObjectMetadata. Metode ini akan menampilkan AmazonServiceException jika kuncinya tidak ada.

private AmazonS3 s3;
...
public boolean exists(String path, String name) {
    try {
        s3.getObjectMetadata(bucket, getS3Path(path) + name); 
    } catch(AmazonServiceException e) {
        return false;
    }
    return true;
}
pengguna979051
sumber
2
getObject juga melempar AmazonServiceException, jadi mengapa melakukan dua panggilan? Juga, bagaimana saya tahu bahwa objek tersebut tidak ada dari kutipan ini? Mungkin itu karena kesalahan S3 lainnya dan objek memang ditemukan.
AlikElzin-kilaka
5
Jangan gunakan penanganan pengecualian untuk kontrol program.
Simon Peck
4
@ AlikElzin-kilaka, karena getObject () berarti Anda harus mengunduh konten objek, yang berpotensi sangat besar.
Jason Nichols
18
@SimonPeck, itu tidak ideal, tetapi ketika Amazon menawarkan metode existing () yang sesuai, maka poin Anda valid.
Jason Nichols
4
@ SimonPeck apakah Anda memiliki alternatif dalam kasus ini? Ini bukanlah penyalahgunaan pengecualian yang mencolok sebagai aliran kontrol program ... ini sederhana, akurat pada apa yang dilakukannya, dan aman. Jika Anda mengambil ide Anda secara ekstrem (seperti yang tampaknya Anda lakukan jika menurut Anda cuplikan kode ini menyalahgunakan pengecualian), lalu mengapa memiliki pengecualian sama sekali dalam suatu bahasa? Alih-alih melempar pengecualian untuk mengingatkan program dan mengubah aliran program , runtime seharusnya hanya menghentikan saya kira.
Don Cheadle
17

Di Amazon Java SDK 1.10+, Anda dapat menggunakan getStatusCode()untuk mendapatkan kode status respons HTTP, yaitu 404 jika objek tidak ada.

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()mengkonsumsi lebih sedikit sumber daya, dan respons tidak perlu ditutup seperti getObject().


Di versi sebelumnya, Anda dapat menggunakan getErrorCode()dan memeriksa string yang sesuai (tergantung versinya).

Paul Draper
sumber
Jika objek s3 Anda tidak memiliki data meta yang dilampirkan padanya, maka getObjectMetadata akan menampilkan kesalahan 404 meskipun objek s3 ada. Saya tidak akan merekomendasikan ini jika tujuannya adalah untuk memeriksa keberadaan objek s3.
Ashish Goel
@AshishGoel, akan selalu ada metadata, jika objek tersebut ada. Faktanya, permintaan HTTP yang mendasarinya hanyalah HEAD ke URL objek.
Paul Draper
5

Gunakan ListObjectsRequest setting Prefix sebagai kunci Anda.

Kode .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;
            }
        }
    }.
pengguna34402
sumber
7
PERINGATAN! Amazon mengenakan biaya tambahan untuk setiap panggilan DAFTAR! Metode ini baik-baik saja, tetapi jangan gunakan untuk memeriksa apakah file ada sebelum mengunduhnya.
pengguna34402
Ini bukan cara yang baik untuk mengetahui jika file ada karena mendapatkan semua objek yang cocok dengan awalan. Jika Anda memiliki beberapa file yang dimulai dengan kunci, itu akan mengunduh semua objek, termasuk yang Anda tentukan.
Crypth
Mengenai biaya LIST vs GET: perhatikan bahwa Anda juga akan dikenakan biaya untuk setiap data yang ditransfer keluar. Jadi, jika sangat tidak mungkin file tersebut ada (misalnya, Anda membuat UUID acak sebagai kunci dan ingin memastikannya belum digunakan) maka GET jauh lebih murah. Tetapi jika file berukuran 0,5 MB dan memiliki 11% kemungkinan sudah ada, maka LIST terlihat sedikit lebih murah. Sama jika file berukuran 0,1 MB dan memiliki peluang 52% ... Semakin besar file, semakin cepat LIST menjadi lebih murah. Tetapi sekali lagi, skenario umum adalah menguji kunci UUID yang baru dibuat, dan GET lebih murah untuk itu.
Bampfer
5

Untuk PHP (Saya tahu pertanyaannya adalah Java, tetapi Google membawa saya ke sini), Anda dapat menggunakan pembungkus aliran dan file_exists

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

Kode java ini memeriksa apakah kunci (file) ada di 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;
}
c0mrade
sumber
2
Ini seharusnya berfungsi, tetapi juga harus lambat jika ada ribuan atau file, dan untuk setiap loop file akan diperlukan.
Danijel
seperti yang dikatakan @Danijel, ini memang akan menentukan apakah atau tidak sebuah objek dari kunci yang diberikan ada, tapi untuk melakukannya sehingga harus loop atas berpotensi puluhan ribu benda di S3 sebelum menentukan apakah atau tidak itu ada
Don Cheadle
1
Saya tidak setuju dengan @Danijel dan mmcrae tentang lambatnya ini. Permintaan listObjects menentukan .withPrefix (file) sehingga harus mengembalikan paling banyak satu file yang cocok, kecuali ada file lain yang namanya dimulai dengan nama file target.
davidwebster48
3

Hancurkan jalan Anda menjadi keranjang dan objek. Menguji keranjang menggunakan metode doesBucketExist, Menguji objek menggunakan ukuran daftar (0 jika tidak ada). Jadi kode ini akan melakukan:

String bucket = ...;
String objectInBucket = ...;
AmazonS3 s3 = new AmazonS3Client(...);
return s3.doesBucketExist(bucket) 
       && !s3.listObjects(bucket, objectInBucket).getObjectSummaries().isEmpty();
roee
sumber
Mudah dan simpel. Terima kasih
Thermech
3

Menggunakan Object isting. Fungsi Java untuk memeriksa apakah kunci yang ditentukan ada di 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
sumber
2

Cara yang tepat untuk melakukannya di SDK V2, tanpa kelebihan beban untuk benar-benar mendapatkan objek, adalah dengan menggunakan S3Client.headObject . Secara resmi didukung oleh AWS Change Log .

Kode contoh:

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
sumber
1

Ada cara mudah untuk melakukannya menggunakan metode isObjectInBucket () jetS3t API.

Kode sampel:

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
sumber
Itu melakukan panggilan get-metadata yang sama di bawah tenda + tangkapan pengecualian: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…
alexandroid
1

Jawaban lainnya adalah untuk AWS SDK v1. Berikut adalah metode untuk AWS SDK v2 (saat ini 2.3.9).

Perhatikan bahwa getObjectMetadatadan doesObjectExistmetode saat ini tidak ada di v2 SDK! Jadi itu bukan lagi pilihan. Kami terpaksa menggunakan salah satu getObjectatau listObjects.

listObjectspanggilan saat ini 12,5 kali lebih mahal untuk dibuat daripada getObject. Namun AWS juga mengenakan biaya untuk data apa pun yang diunduh, yang menaikkan harga getObject jika file tersebut ada . Selama file tersebut sangat tidak mungkin ada (misalnya, Anda telah membuat kunci UUID baru secara acak dan hanya perlu memeriksa ulang bahwa itu tidak diambil) lalu memanggilgetObject secara signifikan lebih murah menurut perhitungan saya.

Untuk berjaga-jaga, saya menambahkan range()spesifikasi untuk meminta AWS hanya mengirim beberapa byte file. Sejauh yang saya tahu, SDK akan selalu menghormati ini dan tidak menagih Anda untuk mengunduh seluruh file. Tetapi saya belum memverifikasi bahwa jadi andalkan perilaku itu dengan risiko Anda sendiri! (Selain itu, saya tidak yakin bagaimana rangeperilakunya jika objek S3 memiliki panjang 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;
    }
}

Catatan: kode ini mengasumsikan s3Clientdan logdideklarasikan serta diinisialisasi di tempat lain. Metode mengembalikan boolean, tetapi dapat memunculkan pengecualian.

Bampfer
sumber
Sepertinya sekarang ada s3Client.headObject()di V2 untuk melakukan ini: stackoverflow.com/a/56949742/9814131 , dan Anda akan memeriksa S3Exceptionkode status 404 untuk memeriksa apakah objek ada sesuai dengan masalah github github.com/aws/aws-sdk- java-v2 / issues / 297 . Tapi saya rasa milik Anda lebih progresif karena memiliki overhead yang sangat sedikit seperti 0-3 byte.
Shaung Cheng
1

Saya juga menghadapi masalah ini saat saya menggunakannya

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

Saya mendapat kunci kesalahan tidak ditemukan

Saat aku memukul dan mencoba

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

berhasil, kode ini bekerja dengan 1.9 jar jika tidak perbarui ke 1.11 dan gunakan doesObjectExist seperti yang dikatakan di atas

Aakash Sharma
sumber
1

Seperti yang disebutkan orang lain, untuk AWS S3 Java SDK 2.10+ Anda dapat menggunakan objek HeadObjectRequest untuk memeriksa apakah ada file di bucket S3 Anda. Ini akan bertindak seperti permintaan GET tanpa benar-benar mendapatkan file tersebut.

Contoh kode karena orang lain belum benar-benar menambahkan kode apa pun di atas:

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
sumber
melempar NoSuchKeyException
Andrii Karaivanskyi
Itu karena kuncinya tidak ada. Itulah yang Anda cari. Jadi tangani pengecualian itu dan kembalikan false untuk itu. Saya telah memperbarui kode di atas untuk menyertakan coba / tangkap.
Navigatron
Maka Anda tidak perlu headObjectResponsesama sekali. throws Exceptiontidak dibutuhkan juga.
Andrii Karaivanskyi
@AndriiKaraivanskyi itu hanya contoh, saya tidak mengujinya.
Navigatron
headObjectResponse.sdkHttpResponse () .isSuccessful (); selalu berhasil apakah file tersebut ada atau tidak?
tandai
0

Atau Anda dapat menggunakan Minio-Java klien , Sumber Terbuka dan kompatibel dengan API AWS S3.

Anda dapat menggunakan Minio-Java StatObject.java contoh untuk hal yang sama.

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

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

impor org.xmlpull.v1.XmlPullParserException;


kelas publik GetObject {
  public static void main (String [] args)
    melempar NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
    // Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY, dan my-bucketname adalah
    // nilai dummy, harap ganti dengan nilai aslinya.
    // Tetapkan titik akhir s3, wilayah dihitung secara otomatis
    MinioClient s3Client = new MinioClient ("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY");
    Arus InputStream = s3Client.getObject ("nama-keranjang-saya", "nama-objek-saya");

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

    stream.close ();
  }
}

Saya harap ini membantu.

Penafian: Saya bekerja untuk Minio

koolhead17
sumber