Javaを使用して、指定されたキーが特定のS3バケットに存在するかどうかを確認する方法


87

Javaを使用して、特定のバケットにキーが存在するかどうかを確認したいと思います。APIを見ましたが、便利なメソッドはありません。使用しようとしましgetObjectたが、例外が発生しました。


2
将来的には、..あなたが..私は仮定に基づいて回答を提供してきました例外何であったかのようなより多くの情報を提供してください
sethu

4
参考:この質問では、受け入れられた回答は最良の回答ではありません。
マラナ2016年

回答:


3

jets3tライブラリを使用します。AWSSDKよりもはるかに簡単で堅牢です。このライブラリを使用すると、s3service.getObjectDetails()を呼び出すことができます。これにより、オブジェクトの詳細(コンテンツではなく)のみがチェックおよび取得されます。オブジェクトが欠落している場合は404をスローします。したがって、その例外をキャッチして、アプリで処理できます。

ただし、これを機能させるには、そのバケットのユーザーがListBucketにアクセスできる必要があります。GetObjectアクセスだけでは機能しません。その理由は、ListBucketにアクセスできない場合、Amazonはキーの存在を確認できないようにするためです。キーが存在するかどうかを知るだけで、悪意のあるユーザーにとっても十分な場合があります。したがって、ListBucketにアクセスできない限り、アクセスすることはできません。


4
すべて-以下のこの質問に対する更新された回答を参照してください:stackoverflow.com/a/36653034/49678
alexandroid

3
jets3tは古い非推奨のライブラリです。代わりに、aws-java-sdkを使用してください。
the_storyteller

「より簡単でより堅牢」は非常に主観的です
LeoRomanovsky19年

291

今ありますdoesObjectExistの公式Java APIの方法が。

楽しい!


13
1.10.51
Steamer25

4
これに賛成してトップに立つ必要があります!
SureshS 2016年

2
正しいことはこれを受け入れられた答えにすることですが、OPだけがそれを行うことができます。meta.stackexchange.com/questions/120568/...
malana

4
これはネットワーク呼び出しを行う必要がありますが、オブジェクトがたくさんある場合はコストがかかります...残念ながら、メタデータ要求でnullを返すことはできません。
ジョエル

9
AmazonがdoesObjectExist2.xSDK(現在はv2.3.9)から削除されたようです。
Bampfer

59

更新:

それをチェックするための新しいAPIがあるようです。このページの別の回答を参照してください:https//stackoverflow.com/a/36653034/435605

元の投稿:

使用する 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);
}

例外についての注意:例外はフロー制御に使用すべきではないことを私は知っています。問題は、AmazonがこのフローをチェックするためのAPIを提供していなかったことです。例外に関するドキュメントだけです。


14
プログラム制御に例外処理を使用しないでください。
サイモンペック

34
@SimonPeck:その通りです。問題は、AmazonがこのフローをチェックするためのAPIを提供していなかったことです。例外に関するドキュメントだけです。賛成票を投じない場合は、反対票を削除してください。
AlikElzin-kilaka 2013

1
これは、JavaSDKには当てはまらないようです。myerrorMessageが「NotFound」に設定されているのがわかりerrorCodeますが、はnullです。
bstempi 2014年

3
ステータスコード404を探しに行きます。文字列を見るよりも堅牢なようです
Oskar Kjellin 2014年

2
@rboarmanによるコメントは正しくありません-ですNoSuchKey。S3エラーコードの明確なリストについては、ドキュメントを参照してください:docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Allen George

22

AWS SDKを使用するには、getObjectMetadataメソッドを使用します。キーが存在しない場合、メソッドはAmazonServiceExceptionをスローします。

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もスローしますが、なぜ2つの呼び出しを行うのでしょうか。また、この例外からオブジェクトが存在しないことをどのように知ることができますか?おそらく、別のS3エラーが原因であり、オブジェクトが実際に見つかりました。
AlikElzin-kilaka 2013

5
プログラム制御に例外処理を使用しないでください。
サイモンペック

4
@ AlikElzin-kilaka、getObject()はオブジェクトのコンテンツをダウンロードする必要があることを意味するため、巨大になる可能性があります。
ジェイソンニコルズ

18
@SimonPeck、それは理想的ではありませんが、Amazonが適切なexists()メソッドを提供する場合、あなたのポイントは有効です。
ジェイソンニコルズ

4
@SimonPeckこの場合の代替手段はありますか?これは、プログラム制御フローとしての例外の露骨な乱用ではありません...これは単純で、実行内容が正確で、安全です。アイデアを極端に考えた場合(このコードスニペットが例外を悪用していると思われる場合と同じように)、なぜある言語で例外があるのでしょうか。例外スローしてプログラムに警告し、プログラムのフローを変更するのではなく、ランタイムは終了する必要があると思います。
ドン・チードル

17

Amazon Java SDK 1.10+ではgetStatusCode()、HTTP応答のステータスコードを取得するために使用できます。オブジェクトが存在しない場合は404になります。

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()消費するリソースが少なく、のように応答を閉じる必要はありませんgetObject()


以前のバージョンでgetErrorCode()は、適切な文字列を使用して確認できます(バージョンによって異なります)。


s3オブジェクトにメタデータが添付されていない場合、s3オブジェクトが存在していても、getObjectMetadataは404エラーをスローします。目的がs3オブジェクトの存在を確認することである場合、これはお勧めしません。
Ashish Goel 2016

@AshishGoel、オブジェクトが存在する場合、常にメタデータが存在します。実際、基になるHTTPリクエストは、オブジェクトのURLへの単なるHEADです。
ポール・ドレイパー

5

ListObjectsRequest設定プレフィックスをキーとして使用します。

.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
警告!Amazonは、LIST呼び出しごとに追加料金を請求します!この方法は問題ありませんが、ダウンロードする前にファイルが存在するかどうかを確認するために使用しないでください。
user34402 2013

これは、プレフィックスに一致するすべてのオブジェクトを取得するため、ファイルが存在するかどうかを取得するための良い方法ではありません。キーで始まるファイルが複数ある場合は、指定したオブジェクトを含むすべてのオブジェクトがダウンロードされます。
Crypth 2013

LISTとGETのコストについて:転送されたデータに対しても課金されることに注意してください。したがって、ファイルが存在する可能性が非常に低い場合(たとえば、ランダムなUUIDをキーとして生成し、それがまだ使用されていないことを確認したい場合)、GETの方がはるかに安価です。ただし、ファイルが0.5 MBで、すでに存在する可能性が11%ある場合、LISTは少し安く見えます。ファイルが0.1MBで、52%の確率で存在する場合も同様です...ファイルが大きいほど、LISTの価格が早くなります。ただし、一般的なシナリオは、新しく生成されたUUIDキーをテストすることであり、GETの方が安価です。
Bampfer

5

PHPの場合(質問はJavaですが、Googleが私をここに連れてきました)、ストリームラッパーとfile_existsを使用できます

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

4

このJavaコードは、キー(ファイル)が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;
}

2
これは機能するはずですが、数千またはファイルがあり、ファイルごとにループが必要になる場合にも遅くなるはずです。
ダニエル2014

@Danijelが言ったように、これは確かに特定のキーのオブジェクトが存在するかどうかを判断しますが、そうするためには、存在するかどうかを判断する前に、S3で潜在的に数万のオブジェクトループする必要があります
Don Cheadle

1
これが遅いことについて、@ Danijelとmmcraeに同意しません。listObjectsリクエストは.withPrefix(file)を指定するため、ターゲットファイルの名前で始まる名前の他のファイルがない限り、一致するファイルを1つだけ返す必要があります。
davidwebster48 2015

3

パスをバケットとオブジェクトに分割します。メソッドを使用してバケットをdoesBucketExistテストし、リストのサイズを使用してオブジェクトをテストします(存在しない場合は0)。したがって、このコードは次のようになります。

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

簡単でシンプル。ありがとう
Thermech 2016年

3

オブジェクトの使用。指定されたキーがAWSS3に存在するかどうかを確認するJava関数。

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

jetS3t APIのisObjectInBucket()メソッドを使用してこれを行う簡単な方法があります。

サンプルコード:

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

        }

これは、内部で
alexandroid

1

その他の回答はAWSSDKv1用です。AWS SDK v2(現在は2.3.9)のメソッドは次のとおりです。

getObjectMetadataおよびdoesObjectExistメソッドは現在v2SDKに含まれていないことに注意してください。したがって、これらはもはやオプションではありません。getObjectまたはのいずれかを使用する必要がありlistObjectsます。

listObjects現在、通話料金はgetObject。より12.5倍高くなっています。ただし、AWSはダウンロードされたデータに対しても課金するためgetObject 、ファイルが存在する場合の料金が高くなります。ファイルが存在する可能性が非常に低い限り(たとえば、新しいUUIDキーをランダムに生成し、それが取得されていないことを再確認する必要がある場合)、getObject私の計算ではは大幅に安価です。

念のため、range()AWSにファイルの数バイトのみを送信するように要求する仕様を追加しました。私の知る限り、SDKは常にこれを尊重し、ファイル全体のダウンロードに対して料金を請求することはありません。しかし、私はそれを確認していませんので、あなた自身の責任でその行動に頼ってください!(また、rangeS3オブジェクトの長さが0バイトの場合の動作もわかりません。)

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

注:このコードはs3Clientlog他の場所で宣言および初期化されていることを前提としています。メソッドはブール値を返しますが、例外をスローする可能性があります。


s3Client.headObject()これを行うためのがV2にあるようです:stackoverflow.com/a/56949742/9814131、そしてS3Exceptiongithubの問題github.com/aws/aws-sdk-に従ってオブジェクトが存在するかどうかを確認するためにのステータスコード404をチェックしますjava-v2 / issues / 297。ただし、オーバーヘッドが0〜3バイトと非常に少ないため、より進歩的だと思います。
Shaung Cheng


1

私が使用したときにもこの問題に直面しました

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

エラーキーが見つかりません

叩いて試してみると

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

それは機能しました、このコードは1.9 jarで機能しています、それ以外の場合は1.11に更新し、上記のようにdoesObjectExistを使用します


1

他の人が言及したように、AWS S3 Java SDK 2.10+の場合、HeadObjectRequestを使用できますオブジェクトをして、S3バケットにファイルがあるかどうかを確認できます。これは、実際にファイルを取得することなく、GETリクエストのように機能します。

他の人が実際に上記のコードを追加していないため、サンプルコード:

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

NoSuchKeyExceptionをスローします
Andrii Karaivanskyi

キーが存在しないためです。それはまさにあなたが探しているものです。したがって、その例外を処理し、falseを返します。上記のコードを更新して、try / catchを含めました。
Navigatron

そうすれば、まったく必要ありませんheadObjectResponsethrows Exception同様に必要ありません。
Andrii Karaivanskyi

@AndriiKaraivanskyiは単なる例であり、テストはしていません。
Navigatron

headObjectResponse.sdkHttpResponse()。isSuccessful(); ファイルが存在するかどうかにかかわらず、常に成功していますか?
マーク

0

または、Minio-Javaクライアントライブラリとそのオープンソースを使用して、AWS S3APIと互換性を持たせることもできます。

Minio-JavaStatObject.javaの例を同じように使用できます。

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

インポートjava.io.InputStream;
インポートjava.io.IOException;
インポートjava.security.NoSuchAlgorithmException;
インポートjava.security.InvalidKeyException;

import org.xmlpull.v1.XmlPullParserException;


パブリッククラスGetObject {
  public static void main(String [] args)
    NoSuchAlgorithmException、IOException、InvalidKeyException、XmlPullParserException、MinioException {をスローします
    //注:YOUR-ACCESSKEYID、YOUR-SECRETACCESSKEY、my-bucketnameは
    //ダミー値。元の値に置き換えてください。
    // s3エンドポイントを設定し、領域は自動的に計算されます
    MinioClient s3Client = new MinioClient( "https://s3.amazonaws.com"、 "YOUR-ACCESSKEYID"、 "YOUR-SECRETACCESSKEY");
    InputStreamストリーム= s3Client.getObject( "my-bucketname"、 "my-objectname");

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

    stream.close();
  }
}

お役に立てば幸いです。

免責事項:私はMinioで働いています

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.