Java on Azure 実践開発【Blob Upload ⓪: はじめに】


## はじめに

今回から５回に分けて、Azure で Java アプリケーションを開発する際に現場で役立ちそうなポイントを、実装上のハマりどころなどを交えながらご紹介いたします。

第一弾のシナリオは、「**Blob ストレージへのアップロード**」です。

サンプルコードは以下に格納しています。

参考: 『[kohei3110/JavaOnAzure-BlobStorage](https://github.com/kohei3110/JavaOnAzure-BlobStorage)』

## シナリオ概説

本シリーズでは、Blob Storage へのアップロードについて、Azure を使った Java アプリケーションのサンプルコードを使用しながら、開発方法をご紹介します。

一口に Blob アップロードといっても、あらゆる方式があるかと思います。

まず、クライアントから Blob をアップロードする場合、SAS を使ってアップロードする選択肢があります。サーバーを経由せず、Blob ストレージとクライアントのみの通信になるため、パフォーマンスが他の選択肢に比べてよいです。SPA からアップロードする場合は、まずは第一選択肢となるでしょう。

一方、クライアントにて SAS の使用が懸念される場合（例: SAS の流出を懸念する場合等）は、サーバーを経由してアップロードすることになります。サーバーを経由し、ストリームライクにアップロードする構成や、サーバーに一時的にファイルを保存し、あとでサーバーからアップロードする構成などが挙げられます。ただし、サーバーを経由するため、リソースや帯域を消費してしまうというデメリットが考えられます。

今回は、Blob アップロードのシナリオを、以下 4 つに区分しました。

| No. | シナリオ |  概要 | 記事 URL |
| :--- | :--- | :--- | :--- |
| 1 | サーバー経由でのストリームライクなアップロード | SAS の流出を懸念する場合、サーバー経由でのストリームライクなアップロード | [Java on Azure 実践開発【Blob Upload ①: ストリームライクなアップロード編】](https://qiita.com/kk31108424/items/63ab4fdd0492e344573a) |
| 2 | サーバーに一時保存後アップロード | アプリケーションサーバーのローカルストレージにファイルを一度保存後、アップロード（クライアントからは同期に見える） | [Java on Azure 実践開発【Blob Upload ②: App Service ローカル保存後アップロード編】](https://qiita.com/kk31108424/items/366649945294a50b808a) |
| 3 | サーバーに一時保存後定期アップロード | アプリケーションサーバーのローカルストレージにファイルを一度保存後、**定期ジョブにて**アップロード（クライアントからは非同期に見える） | [Java on Azure 実践開発【Blob Upload ③: 定期アップロード編】](https://qiita.com/kk31108424/items/5182adf18695238fc1e1) |
| 4 | 署名付き URL を使ったアップロード | アプリケーションから返却された署名付き URL を用いて、クライアントからファイルをアップロードする | [Java on Azure 実践開発【Blob Upload ④: 署名付き URL を使ったアップロード編】](https://qiita.com/kk31108424/items/314027635b4518b2a6b2) |

以下に、それぞれのシナリオを一枚絵にまとめました。

### 1. サーバー経由でのストリームライクなアップロード

Blob Storage へファイルをストリームライクにアップロードします。想定されるシナリオは、動画などの大きなファイルを、アプリケーション実行環境のローカルファイルストレージには保存せず、直接 Blob ストレージへストリームライクにアップロードする場合などです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/731b541e-1e8c-696f-e46b-8f5e47d1d42d.png)

### 2. サーバーに一時保存後アップロード

アプリケーションサーバーのローカルファイルシステムにファイルを一時保存後、Blob アップロードします。クライアントからは同期的に見える構成です。一般的に、ローカルファイルシステムに依存する構成となり、スケール性に乏しいアーキテクチャとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/abe9941a-70f8-784a-fe4d-786b96bcc22a.png)

### 3. サーバーに一時保存後定期アップロード

アプリケーションサーバーのローカルファイルシステムにファイルを一時保存後、定期ジョブがBlob アップロードします。「2. サーバーに一時保存後アップロード」との違いは、クライアントからは非同期的に見える構成である点です。こちらも、一般的に、ローカルファイルシステムに依存する構成となり、スケール性に乏しいアーキテクチャとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/019c77c2-2f4d-ad6d-c57b-3fe094096839.png)

### 4. 署名付き URL を使ったアップロード

アプリケーションから返される署名付き URL を用いて、クライアントからファイルをストリームアップロードします。SPA では、こちらの構成で Blob アップロードをする場合が一般的です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/68d51e92-27fa-be65-261e-4e2e3fa5adbf.png)


次回以降の記事では、上記４つのシナリオについて、サンプルコードを交えつつ、実装方法を解説していきます。

## サンプルアプリケーション実行環境

App Service にアプリケーションをデプロイし、Blob Storage に対してファイルをアップロードする方式としています。シナリオ 3 「サーバーに一時保存後定期アップロード」で実行する定期ジョブは、Azure Web Jobs を使用しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/3a32e428-c6aa-d8c3-a014-cb85dbeed6ae.png)

### Blob ストレージ

Azure Blob ストレージは、Azure で提供されているオブジェクトストレージです。大量の非構造化データを格納できるよう最適化されています。 

例えば、画像データや動画データをストリーミングアップロードする場合や、アーカイブデータを格納する場合などのシナリオで使用されます。

Blob ストレージに Blob を格納するには、「コンテナ」という、ファイルシステムでいうディレクトリに似た概念のものを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/4c30138b-d825-6d3f-2ba8-c34e2fb918d5.png)

本記事では詳細な説明を割愛するため、Blob ストレージの詳細は、 Docs をご参照ください。

Docs: 『[Azure Blob Storage の概要](https://docs.microsoft.com/ja-jp/azure/storage/blobs/storage-blobs-introduction)』

### App Service

App Service は、Windows、Linux、Docker コンテナ（カスタム コンテナー）の Web アプリケーションを Azure 上にホストすることができる PaaS です。

今回は、Java アプリケーションの実行環境として、App Service を作成しました。Java バージョンは 11 です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/48e45a29-6f9c-0122-0364-ac3f5c643b35.png)


本記事では詳細な説明を割愛するため、詳細は以下の Docs をご参照ください。

Docs: 『[App Service のドキュメント](https://docs.microsoft.com/ja-jp/azure/app-service/)』

### Web Jobs

Web Jobs は、Azure App Serviceの機能のひとつとして用意されているイベント駆動のアプリケーションのプラットフォームです。本記事では詳細な説明を割愛するため、詳細は『[3. 定期アップロード]()』の記事か、Docs をご参照ください。

Docs: 『[Azure App Service で Web ジョブを使用してバックグラウンド タスクを実行する](https://docs.microsoft.com/ja-jp/azure/app-service/webjobs-create)』

### App Service Plan

App Service や Web Jobs、（オプションによっては）Azure Functions の実行環境です。後述する App Service や Web Jobs は、App Service Plan にマウントされたファイルシステムを参照します。

なお、今回は Web Jobs を実行するため、App Service Plan の OS は Windows を指定しています。
<b>※ Linux では Web Jobs が実行不可のため。</b>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/edae7f83-a3e1-31ed-3a7b-60b26a27f31e.png)

## サンプルコードアーキテクチャ

ここからは、サンプルとして作成したアプリケーションのアーキテクチャについて解説していきます。

サンプルアプリケーションは[こちら](https://github.com/kohei3110/JavaOnAzure-BlobStorage)に公開しています。

サンプル: 『[kohei3110/JavaOnAzure-BlobStorage](https://github.com/kohei3110/JavaOnAzure-BlobStorage)』

### アプリケーションアーキテクチャ

レイヤードアーキテクチャを採用し、以下のように分割しています。

| ディレクトリ | 主な責務 |
| :--- | :--- |
| `repository` | データアクセスなど、アプリケーションの外界とのやりとりを担当。 |
| `service` | ビジネスロジックを担当。 |
| `model` | データ構造を定義。 |
| `Factory.java` | ファクトリクラス。インスタンス生成を担当。 |
| `Controller.java` | コントローラクラス。リクエストのルーティングを担当。 |

UML 図は以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/99ff4d6f-8a1f-643c-a3f0-2eb452e3f9e5.png)


今回は、アノテーションを使わずコードを書きました。特定の DI コンテナに依存しない構成とする狙いがあります。

`Controller.java` にて受けたリクエストを、Service 層に流し、Service 層から Repository 層を call する形を採用しています。

```java:Controller.java
    @PostMapping("/uploadstream")
    public ResponseEntity<String> uploadstream(@RequestBody MultipartFile multipartFile) throws Exception {
        try {
            UploadBlobStreamService uploadBlobStreamService = injectUploadBlobStreamService();
            uploadBlobStreamService.streamUpload(multipartFile);  // Service 層を call
            return ResponseEntity.status(HttpStatus.OK).body("upload succeeded");
        } catch (Exception e) {
            logger.warning(e.toString());
            throw new Exception("Upload operation has failed.");
        }
    }
```

`Factory` クラスにより、使用するインスタンスが生成されています。

```java:Factory.java
    private BlobContainerClient blobContainerClient;

・・・

    public Factory(String containerName) {

・・・

        this.blobContainerClient = this.blobServiceClient
            .getBlobContainerClient(containerName);

・・・

    }

・・・

    public BlobContainerClient getBlobContainerClientInstance() {
        return this.blobContainerClient;
    }

・・・
```

### 環境変数

Blob Storage への接続文字列は、環境変数として定義し、アプリケーションから読み込むようにしています。デプロイの手間を減らし、環境ごとに適切な値を読み取るために、このような構成をとっています。

ローカルでお試しする際は、以下の環境変数をセットすることをお忘れなく。

```sh:env.sample.sh
export STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=https;BlobEndpoint=https://xxxxxxxxxxxxx.blob.core.windows.net/;AccountName=xxxxxxxxxxxxx;AccountKey=xxxxxxxxxxxxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/xxx"
export FILE_LOCATION="C:\\hogehoge"
```

Java に関わらず、App Service 等 Azure 上のアプリケーション実行サービスでももちろん環境変数をセット可能です。

Azure Portal からは、以下の画面にて設定を確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/7428f2ca-0b0e-dcb5-fca2-12f3285ca69d.png)


詳細は、以下の Docs を参考にしてください。

Docs: 『[App Service アプリを構成する](https://docs.microsoft.com/ja-jp/azure/app-service/configure-common?tabs=portal)』

## Tips

こちらでは、構成上の Tips や注意点についてまとめます。

### 最大ファイルサイズ制限

今回の構成には当てはまりませんが、App Service の前に Application Gateway や Front Door を挟む構成の場合、アップロード可能なファイルの最大サイズに制限が出てきます。

本記事執筆時点では、以下の制限となっています。

| App Service の前に配置するリソース | アップロード対象ファイルの最大サイズ |
| :--- | :--- |
| Application Gateway (Standard SKU) | V2 - 4 GB <br>V1 - 2 GB |
| Application Gateway (WAF SKU) | V1 中規模 - 100 MB <br> V1 大規模 - 500 MB<br>V2 - 750 MB<br>V2 (CRS 3.2 以降を使用) - 4 GB|
| Front Door| 2GB |

## おわりに
本記事は、『Java on Azure 実践開発 Blob Upload 編】』の序章として、アプリケーション全体の構造や、今後の実装シナリオについてまとめました。

今後は、サンプルコードを交えつつ、Azure で Java アプリケーションを実行するための Tips をご紹介していきます！お楽しみに～。