Java on Azure 実践開発【Blob Upload ①: ストリームライクなアップロード編】

## はじめに

本記事は、『Java on Azure 実践開発 Blob Upload』シリーズの第1回「ストリームライクなアップロード」編です。
サンプルコードは、[kohei3110/JavaOnAzure-BlobStorage](https://github.com/kohei3110/JavaOnAzure-BlobStorage) に上がっています。

## シナリオ概説

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

一口にファイルアップロードといっても、あらゆる方式があるかと思います。例えば、以下のようなものがあります。

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

本記事では、<b>『1. ストリームライクなアップロード』</b>について実装上のポイントを解説していきます。

## 実装内容

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248756/0ebf5946-16a7-9056-79cc-7407a0808064.png)

## 実装に必要な SDK のクラス

実装にあたって、Azure SDK for Java を使用しました。SDK を使うため、私の方でいくつかクラスを作りました。以下は、その UML 図です。

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

ここからは、実装する際に必要となる SDK のクラスについて解説していきます。

### BlobAsyncClient クラス

`BlobAsyncClient` クラスは、Blob Storage の各オブジェクト（Blob）操作をするためのクライアントを提供します。`BlobAsyncClient` クラスにより、Blob アップロードやダウンロード、Blob のコピーなどの操作が可能になります。

`BlobAsyncClient` は、`BlobClientBuilder` でインスタンス化するか、`getBlobAsyncClient()`メソッドで取得することができます。

なお、Azure では以下３種類の Blob をサポートしています。今回は、ファイルアップロード用途なので、ブロック Blob を使用しました。
これらに対応するクライアントも、Azure SDK for Java で用意されています。
| Blob 種類 | 概要 | クライアント取得メソッド | Javadoc |
| :--- | :--- | :--- | :--- |
|ブロック BLOB | 大量のデータを効率的にアップロードするためのに最適。| [BlobAsyncClient.getBlockBlobAsyncClient Method](https://docs.microsoft.com/ja-jp/java/api/com.azure.storage.blob.blobasyncclient.getblockblobasyncclient?view=azure-java-stable)| [getBlockBlobAsyncClient()](https://javadoc.io/static/com.azure/azure-storage-blob/12.0.0-preview.4/com/azure/storage/blob/BlobAsyncClient.html#getBlockBlobAsyncClient--) |
| 追加 BLOB | 追加操作（仮想マシンのデータのログ記録などのシナリオ）に最適。| [BlobAsyncClient.getAppendBlobAsyncClient Method](https://docs.microsoft.com/ja-jp/java/api/com.azure.storage.blob.blobasyncclient.getappendblobasyncclient?view=azure-java-stable) | [getAppendBlobAsyncClient()](https://javadoc.io/static/com.azure/azure-storage-blob/12.0.0-preview.4/com/azure/storage/blob/BlobAsyncClient.html#getAppendBlobAsyncClient--) | 
| ページ BLOB | ランダムな読み取りと書き込みの操作用に最適。| [BlobAsyncClient.getPageBlobAsyncClient Method](https://docs.microsoft.com/ja-jp/java/api/com.azure.storage.blob.blobasyncclient.getpageblobasyncclient?view=azure-java-stable) | [getPageBlobAsyncClient()](https://javadoc.io/static/com.azure/azure-storage-blob/12.0.0-preview.4/com/azure/storage/blob/BlobAsyncClient.html#getPageBlobAsyncClient--) |

参考: 『[BlobAsyncClient Class](https://docs.microsoft.com/en-us/java/api/com.azure.storage.blob.blobasyncclient?view=azure-java-stable)』
JavaDoc: 『[Class BlobAsyncClient](https://azuresdkartifacts.blob.core.windows.net/azure-sdk-for-java/staging/apidocs/com/azure/storage/blob/BlobAsyncClient.html)』

```java:UploadBlobStreamRepository.java
・・・
            BlobAsyncClient blobAsyncClient = new BlobClientBuilder()
                .connectionString(connectionString)
                .containerName(CONTAINER_NAME)
                .blobName(blobName)
                .buildAsyncClient();
・・・
```

### BlobOutputStream クラス

[`BlobOutputStream` クラス](https://docs.microsoft.com/ja-jp/java/api/com.azure.storage.blob.specialized.bloboutputstream?view=azure-java-stable)は、データをBlobにストリームライクにアップロードするためのクラスです。

```java:UploadBlobStreamRepository.java
・・・
        BlobOutputStream blobOutputStream = BlobOutputStream.blockBlobOutputStream(
            blobAsyncClient,   // Blob クライアント
            options,   // 並列処理オプション（ParallelTransferOptions）
            headers,   // Blob HTTP ヘッダー
            null,   // Blob に関連付けるメタデータ
            AccessTier.HOT,    // Blob のアクセス層
            new BlobRequestConditions(),   // リクエスト成功時のオプションを指定（指定例は、本記事にて後述）（BlobRequestConditions）
            null  // 追加コンテキスト（key-value ペア）
        );
・・・
```

### ParallelTransferOptions クラス

`ParallelTransferOptions` クラスは、データ転送処理を並列化するための設定を定義します。

```java:UploadBlobStreamRepository.java
・・・
        ParallelTransferOptions options = new ParallelTransferOptions()
            .setBlockSizeLong((long) (2 * 1024 * 1024))  // blockSize
            .setMaxConcurrency(2)
            .setProgressReceiver(new ProgressReceiver() {

                @Override
                public void reportProgress(long bytesTransferred) {
                    logger.info("uploaded: " + bytesTransferred);
                }
                
            });
・・・
```

## Tips

ここからは、実装上の Tips をご紹介します。

### ファイルアップロードの進捗状況を確認する

`ProgressReceiver` クラスを利用することで、ファイル転送の進捗状況を確認することができます。内部的には、転送された総バイト数とともに `reportProgress()` メソッドが定期的に呼び出されます。実装時は、この`reportProgress()` メソッドをオーバーライドすることで、任意の形式で進捗状況を確認することができます。

今回は、進捗状況をログに標準出力する形としました。

```java:UploadBlobStreamRepository.java

・・・
        ParallelTransferOptions options = new ParallelTransferOptions()
            .setBlockSizeLong((long) (2 * 1024 * 1024))  // blockSize
            .setMaxConcurrency(2)
            .setProgressReceiver(new ProgressReceiver() {

                @Override
                public void reportProgress(long bytesTransferred) {
                    logger.info("uploaded: " + bytesTransferred);
                }
                
            }

・・・
```

出力されるログは以下のようになります。

```
・・・
2022-05-25 13:40:14.729  INFO 28556 --- [ctor-http-nio-1] c.k.j.U.r.UploadBlobStreamRepository     : uploaded: 567
2022-05-25 13:40:14.729  INFO 28556 --- [ctor-http-nio-1] c.k.j.U.r.UploadBlobStreamRepository     : uploaded: 568
2022-05-25 13:40:14.730  INFO 28556 --- [ctor-http-nio-1] c.k.j.U.r.UploadBlobStreamRepository     : uploaded: 569
2022-05-25 13:40:14.731  INFO 28556 --- [ctor-http-nio-1] c.k.j.U.r.UploadBlobStreamRepository     : uploaded: 570
2022-05-25 13:40:14.731  INFO 28556 --- [ctor-http-nio-1] c.k.j.U.r.UploadBlobStreamRepository     : uploaded: 571
・・・
```

## おわりに

本記事は、『Java on Azure 実践開発 Blob Upload 編】①』として、ストリームライクなアップロードについてまとめました。

次回は、アプリケーションサーバーのローカルストレージにファイルを一度保存後、アップロードする方法についてまとめていきます！お楽しみに～。