AWS SDK for Java 2.x の使い方


はじめに

S3 の SDK を久しぶりに見たら、バージョンが2系になり、1系とはガラッと作り替えられていたので、簡単な使い方をメモしておきます。

旧来の com.amazonaws:aws-java-sdk-s3 とはグループID・アーティファクトIDが変わり、以下の 依存となります。

implementation("software.amazon.awssdk:s3:2.20.+")

他のSDKと合わせてバージョン更新されるので、パッチバージョンはすごい勢いで上がります。

他のSDKも使う場合には、BOMがあるので以下のように定義できます。

dependencies {
    implementation platform('software.amazon.awssdk:bom:2.20.+')
    implementation 'software.amazon.awssdk:s3:'
}


バージョン1の時は、単にクライアントのメソッド呼び出しで結果を受け取る形でしたが、バージョン2からは、それぞれの操作に応じたリクエストオブジェクトを介して操作するようになりました。

バージョン2では、独自の HTTP クライアントを設定、ノンブロッキングI/Oのサポート、レスポンスのページ分割などの機能が含まれています。

利用例は以下が参考になります。

github.com


S3 SDK 利用の流れ

S3 SDKは、概ね以下の流れで操作します。

// クレデンシャル
ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.create();

// クライアント生成
S3Client s3 = S3Client.builder()
    .region(Region.AP_NORTHEAST_1)
    .credentialsProvider(credentialsProvider)
    .build();

// リクエスト生成
ListObjectsRequest listObjects = ListObjectsRequest.builder()  
    .bucket(bucketName)  
    .build();  

// 実行してレスポンス取得
ListObjectsResponse res = s3.listObjects(listObjects);  
for (S3Object obj : res.contents()) {  
    ...
}

上記のリクエスト生成はラムダで以下のように書くことができます。

s3.listObjects(req -> req.bucket(bucketName));

多くはこちらのラムダ形式で書くことになるでしょう。


クレデンシャル

クレデンシャルは AwsCredentialsProvider インスタンスを介して設定します。

IAMユーザのアクセスキーとシークレットアクセスキーで認証を行うには AwsBasicCredentials から AwsCredentialsProvider を生成します。

AwsCredentials credentials = AwsBasicCredentials.create(accessKeyId, secretAccessKey);
AwsCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(credentials);

クレデンシャルを指定しない場合は、DefaultCredentialsProvider が使われます。 DefaultCredentialsProvider では、以下の様に定義されたプロバイダに対して、上から順に認証情報を検索し、最初に見つかったものから AwsCredentialsProvider が生成されます。

AwsCredentialsProvider[] credentialsProviders = new AwsCredentialsProvider[] {
    SystemPropertyCredentialsProvider.create(),       // 1. Javaシステムプロパティ
    EnvironmentVariableCredentialsProvider.create(),  // 2. 環境変数
    WebIdentityTokenFileCredentialsProvider.builder() // 3. ウェブアイデンティティトークン
                                           .asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
                                           .build(),
    ProfileCredentialsProvider.builder()              // 4. 共有 credentials と config ファイル
                              .profileFile(builder.profileFile)
                              .profileName(builder.profileName)
                              .build(),
    ContainerCredentialsProvider.builder()            // 5. Amazon ECS コンテナ認証情報
                                .asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
                                .build(),
    InstanceProfileCredentialsProvider.builder()      // 6. Amazon EC2メタデータサービス
                                      .asyncCredentialUpdateEnabled(asyncCredentialUpdateEnabled)
                                      .profileFile(builder.profileFile)
                                      .profileName(builder.profileName)
                                      .build()
};

詳細は認証情報プロバイダーチェーンを参照してください。


S3クライアントの作成

1.x では AmazonS3ClientBuilder を使いましたが、2.x からは S3Client からビルダーを生成してクライアントを構築します。

S3Client s3 = S3Client.builder()  
        .region(Region.AP_NORTHEAST_1)
        .credentialsProvider(credentialsProvider)
        .build();

クライアントの設定は、1.x では ClientConfiguration で行っていましたが、2.x からはそれぞれの用途別に設定が分割されています。例えば以下のような Configuration があります。

  • httpClientBuilder(ApacheHttpClient/NettyNioAsyncHttpClient)
  • ProxyConfiguration
  • ClientOverrideConfiguration
  • RetryPolicy
  • ClientAsyncConfiguration

それぞれの設定を以下のように個別に適用します。

ProxyConfiguration.Builder proxyConfig = ProxyConfiguration.builder();

ApacheHttpClient.Builder httpClientBuilder = ApacheHttpClient.builder()
    .proxyConfiguration(proxyConfig.build());

ClientOverrideConfiguration.Builder overrideConfig =
    ClientOverrideConfiguration.builder();

S3Client s3 = S3Client.builder()
    .httpClientBuilder(httpClientBuilder)
    .overrideConfiguration(overrideConfig.build())
    .build();

httpClientBuilder() では、HTTPクライアントを指定します。現在4種のHTTPクライアントが提供されており、指定しない場合は ApacheHttpClient が使われます。

  • ApacheHttpClient デフォルトの同期HTTPクライアント
  • UrlConnectionHttpClient 軽量な同期HTTPクライアント
  • NettyNioAsyncHttpClient 非同期クライアント(非同期のデフォルト)
  • AwsCrtAsyncHttpClient 起動が早い非同期クライアント(AWS Lambda ではこちらを利用)


オブジェクトのアップロード

PutObjectRequest を使い以下のようになります。

PutObjectRequest putObj = PutObjectRequest.builder()
    .bucket(bucketName)
    .key(objectKey)
    .build();
s3.putObject(putObl, RequestBody.fromFile(new File(objectPath)));

ラムダ形式で以下のように書くこともできます。

s3Client.putObject(req ->
    req.bucket(bucketName).key(objectKey),
    RequestBody.fromBytes(bytes));

メタデータを付与する場合は以下のようになります。

var metadata = Map.of("x-amz-meta-myVal", "test");
s3Client.putObject(req ->
    req.bucket(bucketName).key(objectKey).metadata(metadata),
    RequestBody.fromBytes(bytes));


オブジェクトの取得

GetObjectRequest を使い以下のようになります。

GetObjectRequest req = GetObjectRequest.builder()
    .bucket(bucketName)
    .key(keyName)
    .build();
ResponseBytes<GetObjectResponse> res = s3.getObjectAsBytes(req);
byte[] data = objectBytes.asByteArray();

ラムダ形式で以下のように書くこともできます。

byte[] data = s3.getObjectAsBytes(req ->
        req.bucket(bucketName).key(keyName)).asByteArray();


オブジェクトの一覧

ListObjectsRequest を使い以下のようになります。

ListObjectsRequest listObjects = ListObjectsRequest.builder()
    .bucket(bucketName)
    .build();

ListObjectsResponse res = s3.listObjects(listObjects);
List<S3Object> objects = res.contents();
for (S3Object myValue : objects) {
   // ...
}

listObjectsV2Paginator() でページ毎に処理できます。

s3.listObjectsV2Paginator(req -> req.bucket(bucketName)).stream()
    .flatMap(r -> r.contents().stream())
    .forEach(c -> System.out.println(" Key: " + c.key() + " size = " + c.size()));

デフォルトでは1ページは1,000件となっています( maxKeys() で変更可)。


オブジェクトの削除

DeleteObjectRequest を使い以下のようになります。

DeleteObjectRequest req = DeleteObjectRequest.builder()
    .bucket(bucketName)
    .key(keyName)
    .build();
s3.deleteObject(req);

ラムダ形式で以下のように書くこともできます。

s3Client.deleteObject(req -> req.bucket(bucketName).key(keyName));


S3 Transfer Manager

大容量・大量のファイルの転送を行う場合は、S3 Transfer Manager を使うことでパフォーマンスを改善できます。

S3 Transfer Manager を使うには、以下の依存を追加します。

implementation("software.amazon.awssdk:s3-transfer-manager:2.20.+")
implementation("software.amazon.awssdk.crt:aws-crt:0.27.+")

S3TransferManager は以下のようにしてインスタンスを生成します。

S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
    .credentialsProvider(DefaultCredentialsProvider.create())
    .region(Region.AP_NORTHEAST_1)
    .targetThroughputInGbps(20.0)
    .minimumPartSizeInBytes(8 * MB)
    .build();

S3TransferManager tm = S3TransferManager.builder()
    .s3AsyncClient(s3AsyncClient)
    .build();

S3TransferManager では、転送の進行状況をリアルタイムで監視したり、転送を一時停止して後で再開するなど、柔軟な転送処理が実現できます。


S3 Transfer Manager によるアップロード

S3 Transfer Manager でファイルをアップロードするには UploadFileRequest を使います。

UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
        .putObjectRequest(req -> req.bucket(bucketName).key(keyName))
        .addTransferListener(LoggingTransferListener.create())
        .source(Paths.get("etc.txt"))
        .build();

FileUpload upload = transferManager.uploadFile(uploadFileRequest);
upload.completionFuture().join();


S3 Transfer Manager によるダウンロード

S3 Transfer Manager でファイルをダウンロードするには DownloadFileRequest を使います。

DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder()
        .getObjectRequest(req ->
            req.bucket(bucketName).key(keyName))
               .destination(Paths.get("etc.txt"))
               .addTransferListener(LoggingTransferListener.create())
               .build();

FileDownload download = transferManager.downloadFile(downloadFileRequest);
download.completionFuture().join();


S3 Transfer Manager によるディレクトリアップロード

ディレクトリ内のファイルをまるごとアップロードするには UploadDirectoryRequest を使います。

S3TransferManager tm = S3TransferManager.create();
DirectoryUpload directoryUpload = tm.uploadDirectory(
        UploadDirectoryRequest.builder()
            .source(Paths.get("source/directory"))
            .bucket(bucketName)
            .build());

CompletedDirectoryUpload completedDirectoryUpload = directoryUpload.completionFuture().join();
completedDirectoryUpload.failedTransfers().forEach(System.out::println);


S3 Transfer Manager による全オブジェクトダウンロード

バケット内のオブジェクトを一括してディレクトリにダウンロードするには DownloadDirectoryRequest を使います。

DirectoryDownload directoryDownload = tm.downloadDirectory(
        DownloadDirectoryRequest.builder()
            .destination(Paths.get("destination/directory"))
            .bucket(bucketName)
            .build());

CompletedDirectoryDownload completedDirectoryDownload = directoryDownload.completionFuture().join();
completedDirectoryDownload.failedTransfers().forEach(System.out::println);