1
1

【SpringBoot】AmazonS3にファイルをアップロード&ダウンロードする方法

Last updated at Posted at 2024-07-18

概要

Spring Bootを3系にアップデートするとjavaxjakartaに置き換える必要があるため、javaxが依存関係に含まれるspring-cloud-starter-aws1が使えなくなる(と思ったのですが、手元の環境では普通に動きました…)。
念のため依存関係がjakartaになっているio.awspring.cloud.spring-cloud-aws-starterに変更したのですが、情報が少なかったのでサンプルを公開します。

環境

  • Java17
  • SpringBoot
    • spring-boot-starter-parent:3.2.6
    • spring-cloud-aws-dependencies:3.1.1
      • spring-cloud-aws-starter
      • spring-cloud-aws-starter-s3

サンプル

ストレージクラスにIntelligent-Tieringを指定してAmazon S3にファイル(オブジェクト)をアップロードするサンプルです。
ローカル環境からのアップロードはIntelligent-Tiering、EC2(サーバー)環境からのアップロードはStandard(何も指定しない場合と同じ)で指定する想定です。

※直接関係ない部分は省略しています

pom.xml

AWS SDK For Javaを直接使っても良いと思いますが、今回はspring-cloud-aws-starterを使います。

pom.xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.awspring.cloud</groupId>
            <artifactId>spring-cloud-aws-dependencies</artifactId>
            <version>3.1.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>io.awspring.cloud</groupId>
        <artifactId>spring-cloud-aws-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>io.awspring.cloud</groupId>
        <artifactId>spring-cloud-aws-starter-s3</artifactId>
    </dependency>
</dependencies>

dependencyManagementspring-cloud-aws-dependenciesを指定すると一括でversion指定ができます。

AwsStorageConfig.java

S3ClientをSeriveクラスでAutowiredして使いたいのでConfigを作ってBeanに登録します。EC2上ではIAM Roleで認証情報を取得できます2が、ローカル環境では情報が取れないので明示的に指定します。

AwsStorageConfig.java
package com.tamorieeeen.sample.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
/**
 *
 * @author tamorieeeen
 *
 */
@Configuration
public class AwsStorageConfig {

    @Value("${spring.cloud.aws.credentials.access-key:unknown}")
    private String accessKey;
    @Value("${spring.cloud.aws.credentials.secret-key:unknown}")
    private String secretKey;
    @Value("${spring.cloud.aws.region.static:unknown}")
    private String region;

    /**
     * ローカル用
     */
    @Bean("s3Client")
    @Profile("local")
    public S3Client s3ClientLocal() {

        StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(
                AwsBasicCredentials.create(accessKey, secretKey));

        return S3Client.builder()
                .region(Region.of(region))
                .credentialsProvider(credentialsProvider)
                .build();
    }

    /**
     * EC2(サーバー)用
     */
    @Bean("s3Client")
    @Profile("server")
    public S3Client s3Client() {

        return S3Client.create();
    }
}

application.yml

ローカル用のapplication-local.ymlとEC2(サーバー)用のapplication-sever.ymlの設定例です。

ローカル用

application-local.yml
spring:
  cloud:
    aws:
      credentials:
        instance-profile: false
        access-key: SUMPLEACCESSKEY1234
        secret-key: SumpleSecretKey123456789
      stack.auto: false
      region:
        instance-profile: false
        static: ap-northeast-1
      s3:
        bucket: your-bucket-name
        storage-class: INTELLIGENT_TIERING

EC2(サーバー)用

application-sever.yml
spring:
  cloud:
    aws:
      credentials:
        instance-profile: true
        useDefaultAwsCredentialsChain: true
      stack.auto: false
      region:
        instance-profile: true
      s3:
        bucket: your-bucket-name
        storage-class: STANDARD

AwsStorageService.java

アップロードファイルはMultipartFileで受け取り、ダウンロードは最終的にResponseEntity<byte[]>に詰めて返す想定です。

AwsStorageService.java
package com.tamorieeeen.sample.service;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import software.amazon.awssdk.core.ResponseInputStream;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;



/**
 *
 * @author tamorieeeen
 *
 */
@Service
public class AwsStorageService {

    @Value("${spring.cloud.aws.s3.bucket}")
    private String bucket;
    @Value("${spring.cloud.aws.s3.storage-class}")
    private String storageClass;
    
    @Autowired
    private S3Client s3Client;

    /**
     * ファイルをS3にアップロード
     * @throws IOException
     * @throws SdkClientException
     */
    public void uploadFile(String s3Path, MultipartFile file) throws SdkClientException, IOException {

        PutObjectRequest putObjRequest = PutObjectRequest.builder()
                .bucket(bucket)
                .key(s3Path)
                .storageClass(storageClass)
                .contentType(file.getContentType())
                .contentLength(file.getSize())
                .build();
        byte[] bytes = file.getBytes();
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);) {
            s3Client.putObject(putObjRequest, RequestBody.fromInputStream(inputStream, bytes.length));
        }
    }

    /**
     * S3からファイルをダウンロード
     * @throws IOException
     */
    public byte[] download(String s3Path) throws IOException {

        GetObjectRequest getObjRequest = GetObjectRequest.builder()
                .bucket(bucket)
                .key(s3Path)
                .build();
        try (ResponseInputStream<GetObjectResponse> resInputStream = s3Client.getObject(getObjRequest);) {
            return resInputStream.readAllBytes();
            /*
             * ちなみに
             * ContentType は resInputStream.response().contentType()
             * ContentLength は resInputStream.response().contentLength()
             * で取れます
             */
        }
    }
}

公式のサンプル

情報少ないな~と思いつつ解決した後にAWS公式のサンプルが公開されていることに気付きました。
AWS SDK For Javaのサンプルコードだと思いますが大枠は多分一緒なので、 最初からこれ見てれば楽勝だったのに…! かなり参考になります。

参考

  1. io.awspring.cloudorg.springframework.cloudがありますがどちらでも一緒です

  2. 直接関係ないので省略しますが、別途IAM Roleの設定が必要です

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