5
3

【AWS Java SDK 1 & 2】LocalStackを使ってS3にファイルを保存する

Last updated at Posted at 2022-12-07

目的

投稿時点でAWS for Java SDK 2 の情報がネット上に少ない、探しにくい印象を受けたのでコードを書いてみました。v1で運用してる人が多いのかな。いずれv2が主流になる?既になってる?ので移行の参考になるような記事を今後も追加したいなと考えてます。LocalStackを活用してAWSを無料で遊んでみたい人や、AWSをローカルで動確/テストしたい人向けの記事になりますので、LocalStackなんてケチくさいぞ本番でジャンジャン課金してけばいいんだよって方はブラウザバック安定です(笑)

【追記】
やっぱりサポート切れるじゃん:scream::scream::scream:移行めんどい:innocent:
2025 年 12 月 31 日をもって AWS SDK for Java v1.x のサポートを終了
https://aws.amazon.com/jp/blogs/developer/announcing-end-of-support-for-aws-sdk-for-java-v1-x-on-december-31-2025/

環境

OS:Windows11
java:Amazon Corretto v17
DockerDesktop:v4.25.2

v2の新機能

公式サイトからの引用です。

独自の HTTP クライアントを設定できます。

非同期クライアントが完全に非ブロックとなり、CompletableFutureオブジェクトを返すようになりました。

複数ページを返すオペレーションには、自動ページ分割レスポンスがあります。これにより、後続のページを確認して取得する必要がなくなり、レスポンスをどのように処理するかのコードに集中できます。

AWS Lambda 関数のSDK起動時間のパフォーマンスが強化されました。

バージョン 2.x は、リクエストを作成するための、新しい簡易的な方法をサポートします。

Docker Desktopの導入

Docker Desktop公式サイト
Docker用に仮想環境を構築してくれるソフトウェアです。一時期、有料化で話題になりましたね。LocalStackを動作させるために必要になります。QiitaにDocker環境構築の記事は沢山あるため、ここでは割愛します。

AWS CLIのインストール

AWS公式サイト
上記サイトからWindows用の資材をインストールすればコマンドプロンプトでAWS CLIが使えるようになります。

LocalStackについて

LocalStack公式サイト
AWSサービスをローカルで使うことが出来るエミュレーションサービスです。無料版と有料版がありますが無料でも十分AWSをローカル環境で開発、テスト可能です。とりあえずAWSに触れてみたい方にはオススメのサービスです。serverlessなサービスなら無料でほとんど利用できます。(S3、lamdba、SQS、DynamoDBなど)

LocalStack導入の準備

公式LocalStackがawsコマンドをラッパーしたawslocalコマンドを提供しています。endpoint設定を省略できるだけかもですが少し楽になります。以下のコマンドでインストール可能です。(Windowsの場合はPythonをインストールしないとpipコマンド叩けないです)

pip install awscli-local

LocalStack AWS CLI (awslocal)

awsコマンドでLocalStackで作成したS3のバケットを確認したい場合は

aws s3 ls --endpoint http://localhost:4566

のようにエンドポイントを指定する必要があります。

awscli-localの場合は

awslocal s3 ls

だけになります。

他にもGUIでLocalStackを操作可能な「Localstack Cockpit」なんてツールも提供してますが、まだBeta版なためか私の環境ではエラー吐きまくりでまともに動作しませんでした。。
(LocalStack Cockpit 0.1.1は発行元不明でウイルスチェックに引っ掛かる始末😢😢😢)

LocalStackをDocker上で起動する

docker-compose.ymlを使います。初期ポートは4566です。AWS_ACCESS_KEY_IDやAWS_SECRET_ACCESS_KEYは認証する必要がないためdummyで構いません。以下、S3動作確認時のサンプルになります。

version: '3.8'

services:
  localstack:
    container_name: localstack
    image: localstack/localstack
    ports:
      - "127.0.0.1:4566:4566"            
      - "127.0.0.1:4510-4559:4510-4559"  
    environment:
      - SERVICES=dynamodb,sqs,sns,ses,s3,lamdba
      - AWS_ACCESS_KEY_ID=dummy
      - AWS_SECRET_ACCESS_KEY=dummy
      - AWS_DEFAULT_REGION=ap-northeast-1
      - DATA_DIR=/tmp/localstack/data
    volumes:
      - ./docker/localstack:/docker-entrypoint-initaws.d
      -  /var/run/docker.sock:/var/run/docker.sock

最後に以下のコマンドでコンテナを立ち上げて準備完了です!

docker-compose up -d

最終行にContainer localstack Startedと表示されればOKです。

AWS CLIでバケットを作成する

javaでバケットを作成することも出来ますがコマンドで作成しちゃいます。

aws s3 mb s3://bucket-name --endpoint http://localhost:4566

または

awslocal s3 mb s3://bucket-name

make_bucket: bucket-nameと表示されたら作成完了です。

ローカルファイルをS3にアップロードする

uploadToS3.png
v1とv2のjavaでの実装の違いを確認してみます。

AWS for Java SDK 1.x のコード

認証情報などは引数から渡せるようにしてます。ローカル環境なのでアクセスキーとシークレットキーは"dummy"とかで大丈夫です。公式のsampleソースをもとに作成しましたが、必要な個所をコピペするだけです。

public void UploadObjectToS3(String region,String endpoint,String accessKey,String secretKey,String bucketName,String filePath,String fileName) throws Exception {

    try {
            // S3 クライアント
            AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                    .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(endpoint, region))
                    .withPathStyleAccessEnabled(true)
                    .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(accessKey, secretAccessKey)))
                    .build();

            // ファイルアップロード
            PutObjectResult result = s3Client.putObject(bucketName, fileName, new File(filePath));

        } catch (S3Exception e) {
            // 例外処理
        } catch (SdkClientException e) {
            // 例外処理
        } catch (SdkServiceException e) {
            // 例外処理
        }
}

AWS for Java SDK 2.x のコード

v2からクライアントをcloseしてリソース開放可能になりました。try-with-resources文で実装した方が良いかもです。あとはメソッドチェーンにwithが無くなっているくらいでほとんど差はないですね。v1ではputObjectメソッドの引数にFileクラスを指定出来ましたが、v2ではバイト配列に変換する必要があります。v1とv2でnamespaceが異なります。併用環境は是非v2への移行を。

public void UploadObjectToS3(String accessKey,String secretKey,String endpoint,String bucketName,String filePath,String fileName) throws Exception {

    S3Client s3Client = null;

    try {
            // S3クライアント
            s3Client = S3Client.builder()
        		            .credentialsProvider(() -> (AwsBasicCredentials.create(accessKey,secretAccessKey)))
        		            .region(Region.AP_NORTHEAST_1)
        		            .forcePathStyle(true)
        		            .endpointOverride(URI.create(endpoint))
        		            .build();
        	
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                .bucket(bucketName)
                .key(fileName)
                .build();
        
            File file = new File(filePath);
            byte[] data = Files.readAllBytes(file.toPath());
        
            // ファイルアップロード
            PutObjectResponse response = s3Client.putObject(putObjectRequest, RequestBody.fromBytes(data));

        } catch (S3Exception e) {
            // 例外処理
        } catch (SdkClientException e) {
            // 例外処理
        } catch (SdkServiceException e) {
            // 例外処理
        } finally {
            s3Client.close();
        }
    
}

AWSのサーバーレスサービスは運用コストも安く、コーティングも殆ど発生しないからとっても便利ですね。冪等性とか癖の強い面もあるので扱うのは結構大変ですが。。。

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3