5.4.1. アップロードファイル管理

5.4.1.1. Overview

本ガイドラインでは、Amazon Simple Storage Service(以降、S3)を使用した仮アップロードファイルの保存方法について説明する。

Note

本ガイドラインでは、アップロードファイルをS3のストレージに保存する方法および留意点のみを説明する。

AWS環境におけるファイルアップロードの実装方法は Macchinetta Server Framework for Java (1.x) Development Guideline ファイルアップロード に記載の内容と基本的に同様であり、アップロードファイルの保存先をAWSが提供するストレージサービスであるS3に保存する点のみが異なる。

Note

本ガイドラインでは、仮アップロードを目的としたオブジェクト保存方法の説明に主眼を置くため、S3のオブジェクトのバージョニングについては考慮しない。

AWS環境では、Auto Scalingを使用した動的なスケーリングが可能である。 そのため、仮アップロードファイルはローカルストレージに保存せず、APサーバ外のストレージに保存する必要がある。 AWS環境では、ストレージサービスとしてS3が提供されているため、仮アップロードファイルの保存先として活用できる。 アプリケーションからS3へのアクセスは、Spring Cloud AWSまたはAmazon SDK for Javaを使用して行う。

Screen image of file upload on AWS.

5.4.1.1.1. アプリケーションからS3へのアクセス

アプリケーションからS3へアクセスする仕組みは以下の通り。

Screen image of S3 access structure.
項番 説明
(1)
S3へアクセスを行うクラスにインジェクションしたSimpleStorageResourceLoaderまたはPathMatchingSimpleStorageResourcePatternResolverを使用し、SimpleStorageResourceを取得する。
取得を行う際、内部的にはAmazon SDK for JavaのAmazonS3Clientを呼び出している。
(2)
アプリケーションはSimpleStorageResourceを使用してS3へのアクセスを行う。
(3)
SimpleStorageResourceAmazonS3Clientを使用してS3へのアクセスを行う。
(4)
オブジェクトの削除などの保存/取得以外の操作を行う場合、S3へアクセスを行うクラスにAmazonS3Clientをインジェクションして使用する。
(5)
AmazonS3ClientはS3に対してHTTPリクエストによるオブジェクト操作を行う。

5.4.1.2. How to use

5.4.1.2.1. ライブラリの使い分け

S3へのアクセスはSpring Cloud AWSまたはAmazon SDK for Javaを使用することで実装可能であるが、両者の使い分けについて説明する。

Spring Cloud AWSによる実装では、Spring Frameworkが提供するResourceインタフェースによるリソースアクセスの抽象化が利用可能である。 そのため、実装の標準化の観点からSpring Cloud AWSを使用して実装可能な機能については同ライブラリを使用して実装することが望ましい。 Resourceインタフェースの詳細については、Spring Framework Reference Documentation Resources を参照されたい。

ただし、Spring Cloud AWSでは以下のオブジェクト操作のみ実装可能である。

  • S3へのオブジェクト保存
  • S3からのオブジェクト取得
  • AntパターンによるS3内のオブジェクト検索

オブジェクトの削除やバケット操作などのSpring Cloud AWSでは実装不可能な機能についてはAmazon SDK for Javaを直接使用する必要がある。

Note

ライブラリを併用する場合には実装が煩雑となるため、S3へのアクセスを行う部分はHelperクラスを作成して実装を集約することが望ましい。 ファイル操作の実装方法については ファイル操作の実装を参照されたい。

Note

Spring Cloud AWSではS3へのアクセスを行う際にs3://をプレフィックスとするバケット名とオブジェクトキーを含む文字列を使用する。

例)s3://myBucket/myDirectory/myObject.txt

一方、Amazon SDK for Javaでは、バケット名とオブジェクトキーをそれぞれ指定する必要がある。

  • バケット名 : myBucket
  • オブジェクトキー : myDirectory/myObject.txt

Helperクラスを作成する際は留意すること。

5.4.1.2.2. Spring Cloud AWSの利用

5.4.1.2.2.1. 依存ライブラリの追加

Spring Cloud AWSを利用したS3へのアクセスを行うための依存ライブラリの追加を行う。

  • xxx-domain/pom.xml

    <!-- (1) -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-context</artifactId>
    </dependency>
    
項番 説明
(1)
spring-cloud-contextの依存関係を追加する。

5.4.1.2.2.2. アプリケーションの設定

Spring FrameworkのResourceインタフェースを利用したS3へのアクセスを行うためのBean定義を行う。 Bean定義の詳細については、 Spring Cloud AWS Resource handling を参照されたい。

  • xxx-domain/src/main/resources/META-INF/spring/xxx-domain.xml

    <!-- (1) -->
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/cloud/aws/context http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context.xsd">
    
        <!-- (2) -->
        <aws-context:context-resource-loader/>
    
    項番 属性名 内容
    (1)
    xmlns:aws-context
    AWS Context Namespaceを定義する。
    値としてhttp://www.springframework.org/schema/cloud/aws/contextを指定する。

    xsi:schemaLocation
    スキーマのURLを指定する。
    値にhttp://www.springframework.org/schema/cloud/aws/contexthttp://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context.xsdを追加する。
    (2)
    -
    <aws-context:context-resource-loader/>を利用してResourceLoaderインタフェースを利用したS3へのアクセスを有効化する。

Note

Spring Cloud AWSを利用するための設定を行うことでAmazon SDK for Javaを利用するための設定も包含されるため、追加の設定は不要である。

5.4.1.2.2.3. マルチパートアップロードの利用設定

Spring Cloud AWSでは、S3の機能であるマルチパートアップロード(大容量オブジェクトの分割アップロード)をサポートしている。 S3のマルチパートアップロードについては Amazon Simple Storage Service 開発者ガイド マルチパートアップロードの概要 を参照されたい。

前述の設定に加えて以下の設定を行うことでマルチパートアップロードを有効にすることが出来る。

  • xxx-domain/src/main/resources/META-INF/spring/xxx-domain.xml

    <!-- (1) -->
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context"
        xmlns:task="http://www.springframework.org/schema/task"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/cloud/aws/context http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context.xsd
            http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
    
        <!-- (2) -->
        <aws-context:context-resource-loader task-executor="executor" />
        <task:executor id="executor" pool-size="10" queue-capacity="0" rejection-policy="CALLER_RUNS" />
    
    項番 属性名 内容
    (1)
    xmlns:task
    Task Namespaceを定義する。
    値としてhttp://www.springframework.org/schema/taskを指定する。

    xsi:schemaLocation
    スキーマのURLを指定する。
    値にhttp://www.springframework.org/schema/taskhttp://www.springframework.org/schema/task/spring-task.xsdを追加する。
    (2)
    -
    マルチパートアップロードを行うためのtask-executorを定義する。

詳細については Spring Cloud AWS 9.3.1. Uploading multi-part files を参照されたい。

Warning

マルチパートアップロードを有効にしている場合、

  • 5MB未満のファイルの場合:同期アップロード
  • 5MB以上のファイルの場合:5MB毎にマルチパートアップロード

が行われる。 設定したpool-sizeを上限に、5MB毎にスレッドが作成される。 5MBのしきい値は外部から変更することはできない。

各スレッド毎に最大5MBのメモリ消費が発生するため、サイズの大きなファイルの保存を行う場合は留意すること。 また、キューイングされたリクエストもメモリを消費するため、queue-capacityの値にも留意する必要がある。

マルチパートアップロードの設定を行っていない場合は常に同期アップロードが実行される。

5.4.1.2.2.4. ファイル操作の実装

仮アップロードを行う際のファイル操作の実装方法について説明する。

  • S3へのオブジェクト保存【Spring Cloud AWSを使用】

    S3バケット上の一時ディレクトリにオブジェクトを保存する。

    @Inject
    private ResourceLoader resourceLoader; // (1)
    
    // omitted
    
    MultipartFile uploadFile = memberRegisterForm.getUploadFile();
    String bucketname = "myBucket";
    String objectKey = "tmpDirectory/uploadFile_" + UUID.randomUUID().toString() + ".jpg";
    WritableResource resource = (WritableResource) this.resourceLoader.getResource("s3://" + bucketname + "/" + objectKey); // (2)(3)
    try (InputStream inputStream = uploadFile.getInputStream(); OutputStream outputStream = resource.getOutputStream()) { // (4)
        IOUtils.copy(inputStream, outputStream);
    } catch (IOException e) {
        // omitted
    }
    
項番 説明
(1)
ResourceLoaderのインジェクションを行う。
<aws-context:context-resource-loader/>が有効である場合、ResourceLoaderとしてPathMatchingSimpleStorageResourcePatternResolverが使用される。
(2)
S3へ保存するファイルパスを指定する。
PathMatchingSimpleStorageResourcePatternResolvers3://の接頭辞が付与されたパスをS3へのアクセスと判断する。
(3)
WritableResourceを取得する。
S3へのアクセスを行う場合、WritableResourceとしてSimpleStorageResourceが取得される。
(4)
WritableResourceに対して書き込みを行うことでS3へのファイルアップロードを行う。
本ガイドラインでは、org.apache.commons.io.IOUtilsを使用して書き込みを行っている。

Warning

S3のデータ整合性モデルについて

S3では、高い可用性を実現するために、複数のサーバ間でレプリケーションを行っている。 そのため、オブジェクトに対する変更が完全に反映されるまでの間に、変更を反映中のオブジェクトに対する参照を行った場合に意図しない結果が返却される可能性がある。 S3へのアクセスを実装する際は、S3が提供するデータ整合性モデルを踏まえた実装を行う必要がある。 S3のデータ整合性モデルについては、 Amazon Simple Storage Service ドキュメント 開発者ガイド Amazon S3 のデータ整合性モデル を参照されたい。

S3では、PUTおよびDELETEの上書きについては結果整合性、オブジェクトの新規作成については書き込み後の読み込み整合性が提供される。 従って、ファイルの更新を行う場合はオブジェクトキーを変更して別のS3オブジェクトとしてPUTしたものを参照させることで更新前のファイルの参照を防ぐことが可能となる。 その場合、オブジェクトキーにUUIDを含めるなどでオブジェクトキーが重複しないように留意する必要がある。

  • S3からのファイル取得【Spring Cloud AWSを使用】

    S3バケット上の一時ディレクトリに保存したオブジェクトを取得する。 その後、仮アップロード時と同様に取得したオブジェクトを最終的な保存ディレクトリに保存する。

    @Inject
    private ResourceLoader resourceLoader; // (1)
    
    // omitted
    
    Resource resource = this.resourceLoader.getResource("s3://" + bucketname + "/" + objectKey); // (2)
    
    // omitted
    
項番 説明
(1)
ResourceLoaderのインジェクションを行う。
(2)
S3上のパスを指定し、Resourceを取得する。
取得したResourceに対して必要な処理を行う。

Note

S3から取得したファイルをユーザにダウンロードさせる方法については、 Macchinetta Server Framework for Java (1.x) Development Guideline ファイルダウンロード を参照されたい。

  • S3上の単一ファイル削除【Amazon SDK for Javaを使用】

    アップロードファイルの保存後、S3バケット上の一時ディレクトリに保存したオブジェクトを削除する。

    @Inject
    private AmazonS3 s3client; // (1)
    
    // omitted
    
    s3client.deleteObject(bucketname, objectKey); // (2)
    
項番 説明
(1)
AmazonS3のインジェクションを行う。
ResourceLoaderインタフェースを利用するために<aws-context:context-resource-loader/>を有効にしている場合、ContextResourceLoaderBeanDefinitionParserによりBean定義されたAmazonS3Clientが使用される。
(2)
バケット名、オブジェクトキーを指定してファイルの削除を行う。

5.4.1.3. How to extend

5.4.1.3.1. 仮アップロード以外のファイル操作

代表的なファイル操作の例を以下に示す。

  • S3内のAntパターンによるオブジェクト検索【Spring Cloud AWSを使用】

    @Inject
    private ResourcePatternResolver resourcePatternResolver; // (1)
    
    // omitted
    
    Resource[] result = resourcePatternResolver.getResources("s3://myBucket/*"); // (2)
    
    // omitted
    
項番 説明
(1)
ResourcePatternResolverのインジェクションを行う。
ResourceLoaderインタフェースを利用するために<aws-context:context-resource-loader/>を有効にしている場合、ContextResourceLoaderBeanDefinitionParserによりBean定義されたResourcePatternResolverが使用される。
(2)
S3のパスを指定し、Resourceを配列形式で取得する。
検索に使用する文字列はAntパターンで指定する。
  • S3上の複数ファイル削除【Amazon SDK for Javaを使用】

    @Inject
    private AmazonS3 s3client; // (1)
    
    // omitted
    
    //(2)
    List<String> deleteKeyList = new ArrayList<String>();
    // omitted
    List<KeyVersion> targetKeys = new ArrayList<KeyVersion>();
    for (String deleteKey : deleteKeyList) {
        targetKeys.add(new KeyVersion(deleteKey));
    }
    
    //(3)
    DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest("myBucket");
    deleteObjectsRequest.setKeys(targetKeys);
    s3client.deleteObjects(deleteObjectsRequest);
    
項番 説明
(1)
AmazonS3のインジェクションを行う。
(2)
削除対象オブジェクトキーのリストを生成する。
(3)
削除対象のバケット名を指定してDeleteObjectsRequestを生成し、削除対象オブジェクトキーのリストを設定してファイルの削除を行う。

Note

AmazonS3Clientを使用する場合、ファイル削除の他にもバケット操作などのS3上の様々な操作が可能である。

AmazonS3Clientの詳細についてはAmazonS3ClientJavaDoc を参照されたい。

5.4.1.3.2. 仮アップロード時の不要ファイルのHousekeeping

仮アップロードの仕組みを使用してファイルのアップロードを行う場合に必要なHousekeepingについては、S3の仕組みを利用して行うことができる。

5.4.1.3.2.1. ライフサイクル管理の利用

S3では、バケット内のオブジェクトに対してライフサイクルを指定することができる。 ライフサイクル設定を利用して、作成から一定期間を経過したファイルのアーカイブや削除を行えるため、この機能を利用してHousekeepingを行う。

ライフサイクル管理の詳細については、 Amazon Simple Storage Service 開発者ガイド オブジェクトのライフサイクル管理 を参照されたい。

Note

ライフサイクル管理において指定可能なターゲット(対象オブジェクト)は、バケット全体もしくはプレフィックス指定による絞込のみである。 ワイルドカードによる指定などの複雑な制御は行えないため、バケットの構成やオブジェクトの配置に留意すること。

また、ターゲットに末尾が”/”であるプレフィックスを指定する場合、フォルダ配下のオブジェクトだけではなく、フォルダも削除される。

Note

ライフサイクル管理でHousekeepingの要件を満たせない場合は、オブジェクトの削除機能を実装する必要がある。実装方法については Macchinetta Server Framework for Java (1.x) Development Guideline 仮アップロード時の不要ファイルのHousekeeping を参照されたい。

5.4.1.4. Appendix

5.4.1.4.1. 高いリクエストレートが要求される場合の留意事項

Amazon Simple Storage Service 開発者ガイドにおいて、リクエストレートが高い場合のオブジェクトキーの付与についてのガイドラインが記載されている。

上記のガイドラインに従うことで、パフォーマンスの低下を防ぐことが可能である。