| | | | |

Spring BootでS3へファイルアップロードする方法】API経由方式とPresigned URL方式を比較

Spring Boot で Amazon S3 へファイルをアップロードする方法には大きく※2つの方式があります。一つはアプリケーションが S3Client を使ってサーバ側でファイルを受け受り S3 へ PUT する「API経由方式」、もう一つはサーバ側で期限付きの URL (presigned URL) を発行し、クライアントが直接 S3 へ PUT する「Presigned URL方式」です。本記事では、この※2つの方式のメリット・デメリットを比較しながら、Gradle 環境の Spring Boot アプリケーションでの実装方法を詳細に紹介します。

※2つの方式の概要

・API経由方式:フロントエンドが MultipartFormData などでファイルをサーバへ送信し、Spring Boot アプリが S3Client 経由で S3 にファイルを PUT します。サーバがファイル転送を仲介するため、アップロードサイズやアクセス權限をサーバ側で完全に制御できますが、大宽量のファイルが増えるとサーバの幅度負荷が高くなります。

・Presigned URL方式:サーバはアップロード権限を持つ期限付き URL を発行し、それをクライアントに返します。クライアントはその URL を使って直接 S3 へ PUT/GET を行うため、サーバの幅度を消費しません。大量データやスマートフォンアプリに適していますが、CORS 設定や署名の有効期限などに注意が必要です。

Gradle 依存関係と基本設定

AWS SDK for Java v2 を使う場合、build.gradle に以下の依存関係を追加します:

dependencies {
    implementation platform("software.amazon.awssdk:bom:2.25.14") // BOM でバージョン管理
    implementation "software.amazon.awssdk:s3"
    implementation "software.amazon.awssdk:s3-transfer-manager"  // マルチパートアップロード用
}

また src/main/resources/application.yml で S3 のリージョンや証明情報プロファイルを指定しておくと設定が一緒化できます:

aws:
  region: ap-northeast-1
  s3:
    bucket-name: your-bucket-name
  credentials:
    profile: my-aws-profile

API経由方式の実装例

  1. S3Client の Bean を作成します。必要に応じて S3Configuration をカスタマイズし、path style アクセスや chunked encoding を無効にすることも可能です。
@Configuration
public class S3Config {
    @Value("${aws.region}") private Region region;
    @Bean
    public S3Client s3Client() {
        return S3Client.builder()
            .region(region)
            .build();
    }
}
  1. サービスクラスでファイルをアップロードします。Spring の MultipartFile を受け受り、そのバイト列を PutObjectRequest とともに S3 に送信します。
@Service
public class S3UploadService {
    private final S3Client s3Client;
    @Value("${aws.s3.bucket-name}") private String bucket;

    public S3UploadService(S3Client s3Client) {
        this.s3Client = s3Client;
    }
    public void uploadFile(MultipartFile file, String key) throws IOException {
        PutObjectRequest req = PutObjectRequest.builder()
            .bucket(bucket)
            .key(key)
            .contentType(file.getContentType())
            .build();
        s3Client.putObject(req, RequestBody.fromBytes(file.getBytes()));
    }
}

この方式はサーバがすべてのファイルを中継するため権限管理が容易ですが、ファイルサイズが大きいとメモリ負荷が増えるので TransferManager などのマルチパートアップロードも検討しましPresigned URL方式の実装例

  1. S3Presigner の Bean を用意します。リージョンと証明情報を設定します。
@Configuration
public class S3PresignerConfig {
    @Value("${aws.region}") private Region region;
    @Bean
    public S3Presigner s3Presigner() {
        return S3Presigner.builder()
            .region(region)
            .build();
    }
}
  1. サービスクラスで presigned URL を発行します。ここでは PUT 用の URL を生成します。
@Service
public class PresignedUrlService {
    private final S3Presigner presigner;
    @Value("${aws.s3.bucket-name}") private String bucket;

    public PresignedUrlService(S3Presigner presigner) {
        this.presigner = presigner;
    }
    public URL generateUploadUrl(String key, Duration duration) {
        PutObjectRequest objectRequest = PutObjectRequest.builder()
            .bucket(bucket)
            .key(key)
            .build();
        PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
            .signatureDuration(duration)
            .putObjectRequest(objectRequest)
            .build();
        return presigner.presignPutObject(presignRequest).url();
    }
}
  1. コントローラでこの URL をクライアントに返します。クライアントは取得した URL に対し PUT リクエストを送信するだけで S3 にアップロードできます。CORS 設定や Content-Type ヘッダが一致しないと 403 や SignatureDoesNotMatch エラーになるため、事前にバケットの CORS ポリシーを PUTGET に対応させておきます。

よくあるエラーと解決策

  • 403 Forbidden (SignatureDoesNotMatch)
    • 署名有効期限を過ぎているか、Content-Typex-amz-content-sha256 ヘッダが変わってしまった場合に発生します。URL を発行した際のヘッダとクライアントのリクエストが一致しているか確認しましょう。
  • CORSエラー
    • ブラウザから直接 S3 へ PUT/GET する場合は、S3 バケットの CORS 設定に AllowedOriginsAllowedMethods を追加する必要があります。
  • リージョンの不一致
    • Presigned URL を生成するリージョンとバケットのリージョンが異なるとエラーになります。Region を適切に指定してください。

ローカル環境での検証 (LocalStack/MinIO)

本番環境へ接続する前に、LocalStack や MinIO を利用してローカルで S3 API の動作確認をするのも有効です。LocalStack を Docker で起動すれば、エンドポイントを http://localhost:4566 に変えるだけで API経由方式も Presigned URL方式も試せます。Gradle の testImplementationsoftware.amazon.awssdk:s3 を追加し、Spring のテストからファイル操作を行うと開発効率が向上します。

まとめ

どちらの方式も AWS SDK for Java v2 を使って Spring Boot から簡単に実装できます。ユースケ

API経由方式とPresigned URL方式のファイルアップロード概念図

ースに従って適切な方式を選びましょう。ょう。

API経由方式は実装がシンプルで権限管理も一括できますが、大量ファイルではバックエンドに負荷がかかるため注意が必要です。

Presigned URL方式はクライアントが直接 S3 と通信するためサーバ負荷を大きく削減でき、スマホアプリや大量ファイル向けに適しています。

是非フォローしてください

最新の情報をお伝えします

類似投稿