Cách kiểm tra xem khóa được chỉ định có tồn tại trong nhóm S3 nhất định hay không bằng cách sử dụng Java


87

Tôi muốn kiểm tra xem khóa có tồn tại trong một nhóm nhất định bằng Java hay không. Tôi đã xem xét API nhưng không có bất kỳ phương pháp nào hữu ích. Tôi đã cố gắng sử dụng getObjectnhưng nó đã ném ra một ngoại lệ.


2
Trong tương lai, xin vui lòng cung cấp thêm thông tin như là những gì ngoại trừ bạn có .. Tôi đã cung cấp một câu trả lời dựa trên một giả định ..
sethu

4
FYI: Đối với câu hỏi này, câu trả lời được chấp nhận không phải là câu trả lời tốt nhất.
malana

Câu trả lời:


3

Sử dụng thư viện jets3t. Nó dễ dàng và mạnh mẽ hơn rất nhiều so với AWS sdk. Sử dụng thư viện này, bạn có thể gọi, s3service.getObjectDetails (). Thao tác này sẽ chỉ kiểm tra và truy xuất các chi tiết của đối tượng (không phải nội dung) của đối tượng. Nó sẽ ném 404 nếu đối tượng bị thiếu. Vì vậy, bạn có thể nắm bắt ngoại lệ đó và giải quyết nó trong ứng dụng của mình.

Nhưng để điều này hoạt động, bạn sẽ cần có quyền truy cập ListBucket cho người dùng trên nhóm đó. Chỉ cần truy cập GetObject sẽ không hoạt động. Lý do là Amazon sẽ ngăn bạn kiểm tra sự hiện diện của khóa nếu bạn không có quyền truy cập ListBucket. Chỉ cần biết liệu có khóa hay không, cũng sẽ đủ cho những người dùng độc hại trong một số trường hợp. Do đó, trừ khi họ có quyền truy cập ListBucket, họ sẽ không thể làm như vậy.


4
Tất cả - xem câu trả lời cập nhật cho câu hỏi này bên dưới: stackoverflow.com/a/36653034/49678
alexandroid

3
jets3t là một thư viện cũ không được dùng nữa. Thay vào đó, hãy sử dụng aws-java-sdk.
the_storyteller

"dễ dàng hơn và mạnh mẽ hơn" là rất chủ quan
Leo Romanovsky

291

Hiện có một phương thức doesObjectExist trong API Java chính thức.

Thưởng thức!


13
Nó đã được thêm vào 1.10.51
hấp 25

4
Chúng tôi phải ủng hộ điều này và đưa điều này lên hàng đầu!
SureshS

2
Điều đúng đắn cần làm là biến đây thành câu trả lời được chấp nhận nhưng chỉ OP mới có thể làm được điều đó. meta.stackexchange.com/questions/120568/…
malana

4
Điều này phải thực hiện một cuộc gọi mạng, rất tốn kém nếu bạn có nhiều đối tượng ... Thật tệ là nó không thể trả về null khi yêu cầu siêu dữ liệu.
Joel

9
Có vẻ như Amazon đã xóa doesObjectExistkhỏi SDK 2.x (hiện tại là v2.3.9).
Bampfer

59

Cập nhật:

Có vẻ như có một API mới để kiểm tra điều đó. Xem câu trả lời khác trong trang này: https://stackoverflow.com/a/36653034/435605

Bài gốc:

Sử dụng 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);
}

Lưu ý về ngoại lệ: Tôi biết ngoại lệ không nên được sử dụng để kiểm soát luồng. Vấn đề là Amazon không cung cấp bất kỳ api nào để kiểm tra luồng này - chỉ là tài liệu về ngoại lệ.


14
Không sử dụng xử lý ngoại lệ để kiểm soát chương trình.
Simon Peck

34
@SimonPeck: bạn nói đúng. Vấn đề là Amazon không cung cấp bất kỳ api nào để kiểm tra luồng này - chỉ là tài liệu về ngoại lệ. Vui lòng xóa bỏ phiếu phản đối của bạn nếu không bỏ phiếu thuận.
AlikElzin-kilaka

1
Điều này dường như không còn đúng đối với Java SDK. Tôi thấy rằng của tôi errorMessageđược đặt thành "Không tìm thấy", nhưng errorCodegiá trị là rỗng.
bstempi

3
Tôi sẽ đi cho tìm kiếm các mã trạng thái 404. Có vẻ mạnh mẽ hơn nhìn vào một chuỗi
Oskar Kjellin

2
Nhận xét của @rboarman không chính xác - đúng như vậy NoSuchKey. Để có danh sách chính xác về mã lỗi S3, hãy xem tài liệu: docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George

22

Sử dụng AWS SDK, hãy sử dụng phương thức getObjectMetadata. Phương thức sẽ ném một AmazonServiceException nếu khóa không tồn tại.

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 cũng ném AmazonServiceException, vậy tại sao lại có hai cuộc gọi? Ngoài ra, làm thế nào để tôi biết rằng đối tượng không tồn tại từ sự bài trừ này? Có lẽ đó là do một lỗi S3 khác và đối tượng thực sự được tìm thấy.
AlikElzin-kilaka

5
Không sử dụng xử lý ngoại lệ để kiểm soát chương trình.
Simon Peck

4
@ AlikElzin-kilaka, vì getObject () có nghĩa là bạn phải tải xuống nội dung của đối tượng, có thể rất lớn.
Jason Nichols

18
@SimonPeck, nó không phải là lý tưởng, nhưng khi Amazon cung cấp một phương thức tồn tại () thích hợp, thì quan điểm của bạn là hợp lệ.
Jason Nichols

4
@SimonPeck bạn có giải pháp thay thế trong trường hợp này không? Đây không phải là sự lạm dụng trắng trợn các ngoại lệ như luồng điều khiển chương trình ... điều này rất đơn giản, chính xác với những gì nó thực hiện và an toàn. Nếu bạn coi trọng ý tưởng của mình (dường như bạn đang nghĩ nếu bạn nghĩ đoạn mã này đang lạm dụng ngoại lệ), thì tại sao lại có ngoại lệ trong một ngôn ngữ? Thay vì ném một ngoại lệ để cảnh báo chương trình và thay đổi quy trình của chương trình , tôi cho là thời gian chạy chỉ nên kết thúc.
Don Cheadle

17

Trong Amazon Java SDK 1.10+, bạn có thể sử dụng getStatusCode()để lấy mã trạng thái của phản hồi HTTP, mã này sẽ là 404 nếu đối tượng không tồn tại.

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()tiêu thụ ít tài nguyên hơn và phản hồi không cần phải đóng như vậy getObject().


Trong các phiên bản trước, bạn có thể sử dụng getErrorCode()và kiểm tra chuỗi phù hợp (tùy thuộc vào phiên bản).


Nếu đối tượng s3 của bạn không có bất kỳ siêu dữ liệu nào được đính kèm, thì getObjectMetadata sẽ xuất hiện lỗi 404 ngay cả khi đối tượng s3 tồn tại. Tôi sẽ không khuyến nghị điều này nếu mục tiêu là để kiểm tra sự tồn tại của đối tượng s3.
Ashish Goel

@AshishGoel, sẽ luôn có siêu dữ liệu, nếu đối tượng tồn tại. Trên thực tế, yêu cầu HTTP bên dưới chỉ đơn giản là một ĐẦU đối với URL của đối tượng.
Paul Draper

5

Sử dụng Tiền tố cài đặt ListObjectsRequest làm khóa của bạn.

Mã .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
CẢNH BÁO! Amazon tính thêm phí cho mỗi cuộc gọi LIST! Phương pháp này là ok, nhưng không sử dụng nó để kiểm tra xem tệp có tồn tại hay không trước khi tải xuống.
user34402

Đây không phải là một cách tốt để lấy nếu một tệp tồn tại vì nó nhận được tất cả các đối tượng khớp với tiền tố. Nếu bạn có nhiều tệp bắt đầu bằng khóa, nó sẽ tải xuống tất cả các đối tượng, bao gồm cả đối tượng bạn đã chỉ định.
Crypth

Về chi phí LIST so với GET: lưu ý rằng bạn cũng bị tính phí cho bất kỳ dữ liệu nào được chuyển ra ngoài. Vì vậy, nếu rất ít khả năng tệp tồn tại (ví dụ: bạn đã tạo một UUID ngẫu nhiên làm khóa và muốn đảm bảo rằng nó chưa được sử dụng) thì GET sẽ rẻ hơn nhiều. Nhưng nếu các tệp có dung lượng 0,5 MB và có 11% khả năng đã tồn tại, thì LIST có vẻ rẻ hơn một chút. Tương tự nếu các tệp là 0,1 MB và có 52% cơ hội tồn tại ... Các tệp càng lớn, LIST càng sớm càng rẻ. Nhưng một lần nữa, một tình huống phổ biến là kiểm tra khóa UUID mới được tạo và GET rẻ hơn cho điều đó.
Bampfer

5

Đối với PHP (tôi biết câu hỏi là Java, nhưng Google đã đưa tôi đến đây), bạn có thể sử dụng trình bao bọc luồng và file_exists

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

4

Mã java này kiểm tra xem khóa (tệp) có tồn tại trong thùng s3 hay không.

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
Điều này sẽ hoạt động, nhưng cũng sẽ chậm trong trường hợp có hàng nghìn hoặc tệp và cho mỗi vòng lặp tệp sẽ cần thiết.
Danijel

như @Danijel nói, điều này thực sự sẽ quyết định có hay không một đối tượng của một chìa khóa cho tồn tại, nhưng làm như vậy nó phải vòng qua khả năng hàng chục ngàn các đối tượng trong S3 trước khi xác định có hay không nó tồn tại
Don Cheadle

1
Tôi không đồng ý với @Danijel và mmcrae về việc điều này là chậm. Yêu cầu listObjects chỉ định .withPrefix (tệp) vì vậy nó sẽ trả về tối đa một tệp phù hợp duy nhất, trừ khi có các tệp khác có tên bắt đầu bằng tên của tệp đích.
davidwebster48

3

Chia đường dẫn của bạn thành thùng và đối tượng. Kiểm tra nhóm bằng phương pháp doesBucketExist, Kiểm tra đối tượng bằng cách sử dụng kích thước của danh sách (0 trong trường hợp không tồn tại). Vì vậy, mã này sẽ làm:

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

Dễ dàng và đơn giản. Cảm ơn
Thermech

3

Sử dụng Object isting. Hàm Java để kiểm tra xem khóa được chỉ định có tồn tại trong AWS S3 hay không.

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

1

Có một cách dễ dàng để làm điều đó bằng cách sử dụng phương thức isObjectInBucket () của API jetS3t.

Mã mẫu:

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

        }

Nó thực hiện cùng một cuộc gọi get-metadata dưới mui xe + bắt ngoại lệ: grepcode.com/file/repo1.maven.org/maven2/net.java.dev.jets3t/…
alexandroid

1

Các câu trả lời khác dành cho AWS SDK v1. Đây là một phương pháp cho AWS SDK v2 (hiện tại là 2.3.9).

Lưu ý rằng getObjectMetadatadoesObjectExistcác phương thức hiện không có trong SDK v2! Vì vậy, đó không còn là lựa chọn nữa. Chúng tôi buộc phải sử dụng một trong hai getObjecthoặc listObjects.

listObjectsCác cuộc gọi hiện đang đắt hơn 12,5 lần so với getObject. Nhưng AWS cũng tính phí cho bất kỳ dữ liệu nào được tải xuống, điều này làm tăng giá getObject nếu tệp tồn tại . Miễn là tệp rất khó tồn tại (ví dụ: bạn đã tạo một khóa UUID mới một cách ngẫu nhiên và chỉ cần kiểm tra kỹ xem nó không được sử dụng hay không) thì getObjecttính toán của tôi sẽ rẻ hơn đáng kể.

Tuy nhiên, để an toàn, tôi đã thêm một range()thông số kỹ thuật để yêu cầu AWS chỉ gửi một vài byte tệp. Theo như tôi biết, SDK sẽ luôn tôn trọng điều này và không tính phí bạn tải xuống toàn bộ tệp. Nhưng tôi chưa xác minh điều đó vì vậy hãy dựa vào hành vi đó với rủi ro của riêng bạn! (Ngoài ra, tôi không chắc rangesẽ hoạt động như thế nào nếu đối tượng S3 dài 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;
    }
}

Lưu ý: mã này giả định s3Clientlogđược khai báo và khởi tạo ở nơi khác. Phương thức trả về một boolean, nhưng có thể ném các ngoại lệ.


Có vẻ như bây giờ có một s3Client.headObject()trong V2 để thực hiện việc này: stackoverflow.com/a/56949742/9814131 và bạn sẽ kiểm tra S3Exceptionmã trạng thái của 404 để kiểm tra xem đối tượng có tồn tại theo vấn đề github github.com/aws/aws-sdk- java-v2 / issue / 297 . Nhưng tôi đoán của bạn tiến bộ hơn vì nó có rất ít chi phí là 0-3 byte.
Shaung Cheng


1

Tôi cũng gặp phải vấn đề này khi sử dụng

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

Tôi không tìm thấy khóa báo lỗi

Khi tôi nhấn và thử

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

nó đã hoạt động, mã này đang hoạt động với jar 1.9 nếu không, hãy cập nhật lên 1.11 và sử dụng doesObjectExist như đã nói ở trên


1

Như những người khác đã đề cập, đối với AWS S3 Java SDK 2.10+, bạn có thể sử dụng đối tượng HeadObjectRequest để kiểm tra xem có tệp trong nhóm S3 của bạn hay không. Điều này sẽ hoạt động giống như một yêu cầu GET mà không thực sự nhận được tệp.

Mã mẫu vì những người khác chưa thực sự thêm bất kỳ mã nào ở trên:

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

ném NoSuchKeyException
Andrii Karaivanskyi

Đó là bởi vì chìa khóa không tồn tại. Đó chính xác là những gì bạn đang tìm kiếm. Vì vậy, hãy xử lý ngoại lệ đó và trả về false cho nó. Tôi đã cập nhật mã ở trên để bao gồm thử / bắt.
Navigatron

Sau đó, bạn không cần headObjectResponseở tất cả. throws Exceptioncũng không cần thiết.
Andrii Karaivanskyi

@AndriiKaraivanskyi nó chỉ là một ví dụ, tôi đã không kiểm tra nó.
Navigatron

headObjectResponse.sdkHttpResponse () .isSuccessful (); luôn luôn thành công cho dù tệp tồn tại hay không?
đánh dấu

0

Ngoài ra, bạn có thể sử dụng thư viện máy khách Minio-Java , Mã nguồn mở của nó và tương thích với API AWS S3.

Bạn có thể sử dụng các ví dụ Minio-Java StatObject.java tương tự.

nhập io.minio.MinioClient;
nhập io.minio.errors.MinioException;

nhập java.io.InputStream;
nhập java.io.IOException;
nhập java.security.NoSuchAlgorithmException;
nhập java.security.InvalidKeyException;

nhập org.xmlpull.v1.XmlPullParserException;


lớp công khai GetObject {
  public static void main (String [] args)
    ném NoSuchAlgorithmException, IOException, InvalidKeyException, XmlPullParserException, MinioException {
    // Lưu ý: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY và my-bucketname là
    // giá trị giả, vui lòng thay thế chúng bằng giá trị gốc.
    // Đặt điểm cuối s3, vùng được tính toán tự động
    MinioClient s3Client = new MinioClient ("https://s3.amazonaws.com", "YOUR-ACCESSKEYID", "YOUR-SECRETACCESSKEY");
    InputStream stream = s3Client.getObject ("my-bucketname", "my-objectname");

    byte [] buf = byte mới [16384];
    int bytesRead;
    while ((bytesRead = stream.read (buf, 0, buf.length))> = 0) {
      System.out.println (Chuỗi mới (buf, 0, bytesRead));
    }

    stream.close ();
  }
}

Tôi hy vọng nó sẽ giúp.

Tuyên bố từ chối trách nhiệm: Tôi làm việc cho Minio

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.