外部サービス

【Blazor】Azure Blob Storageに画像をアップロードする手順

azure-blob-storage-upload

Web アプリを作るときに、画像をアップロードする機能が必要になることも多いと思います。

ファイルは外部のファイルストレージサービスにアップロードするのが一般的で、今回は Azure Blob Storage を使用した手順を解説します。

Azure Blob Storage に画像をアップロードする手順

ストレージアカウントの作成

Azure ポータルにアクセスして、ストレージアカウントのメニューから作成をします。

azure-blob-storage-upload

サブスクリプションやリソースグループ、ストレージアカウント名を指定します。

地域は一番近い Japan East を指定し、パフォーマンスと冗長性はテスト用なので一番低いものを選んでおけばいいでしょう。

azure-blob-storage-upload

アクセス層もアクセス頻度の低い「クール」を選択しました。

後の設定は初期値で問題ありませんので、そのまま作成します。

azure-blob-storage-upload

このあと、ストレージアカウントの接続文字列が必要になるので、アクセスキーのメニューからコピーしておきましょう。

azure-blob-storage-upload

Blazor プロジェクトの作成

今回は、ASP.NET Core でホストされた Blazor WebAssembly プロジェクトを作成しました。

azure-blob-storage-upload

NuGet パッケージの追加

Server プロジェクトに対して、Azure.Storage.Blobs の NuGet パッケージを追加します。

このパッケージ追加により、Azure Blob Storage にアップロードするための API が使えるようになります。

azure-blob-storage-upload

接続文字列の設定

Server プロジェクトの appsettings.json に、ConnectionStrings のセクションを作成します。

AzureConnectionString のキーで先ほどコピーした接続文字列を設定してください。

API を実行するときに必要になります。

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "AzureConnectionString": "コピーした接続文字列"
  },
  "AllowedHosts": "*"
}

Controller クラスの作成

Azure にアップロードするための Controller クラスを Server プロジェクトに作成します。

UploadAzureController.cs
using System.Net.Mime;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.AspNetCore.Mvc;

namespace BlazorFileUploadTest.Server.Controllers;

[Route("api/[controller]")]
public class UploadAzureController : ControllerBase
{
    private readonly string azureConnectionString;

    public UploadAzureController(IConfiguration configuration)
    {
        azureConnectionString = configuration.GetConnectionString("AzureConnectionString");
    }

    [HttpPost("[action]")]
    [Produces(MediaTypeNames.Application.Json)]
    public async Task<ActionResult<IEnumerable<string>>> Upload([FromForm(Name = "images")] IFormFileCollection files)
    {
        List<string> uploadUrls = new();

        foreach (var file in files)
        {
            var container = new BlobContainerClient(azureConnectionString, "upload-container");

            // コンテナが作成されていなければ作成
            var createResponse = await container.CreateIfNotExistsAsync();

            // BLOB へのアクセス許可
            if (createResponse != null && createResponse.GetRawResponse().Status == 201)
                await container.SetAccessPolicyAsync(PublicAccessType.Blob);

            // フォルダ階層の指定
            var blob = container.GetBlobClient($"images/{Guid.NewGuid()}/{DateTime.Now:yyyyMMddHHmmssfff}");

            // すでにファイルがあれば削除
            await blob.DeleteIfExistsAsync(DeleteSnapshotsOption.IncludeSnapshots);

            // BLOB にアップロード
            using (var fileStream = file.OpenReadStream())
            {
                await blob.UploadAsync(fileStream, new BlobHttpHeaders { ContentType = file.ContentType });
            }
            uploadUrls.Add(blob.Uri.ToString());
        }

        // アップロードしたファイルの URL を返却
        return Ok(uploadUrls);
    }

}

ファイルをアップロードするためには、コンテナーを作る必要があります。

コンテナー名は小文字とする必要があり、今回は upload-container という名前にしました。

コンテナーの中には階層を作ることができるので、Guid の下にタイムスタンプのファイル名でアップロードすることにしました。

実際には、ユーザー ID を階層にするとわかりやすいでしょう。

ファイルをアップロードした後は、ファイルの URL を返却しています。

画面の作成

ファイルをアップロードする画面を作成していきます。

@page "/file-upload"

@inject HttpClient HttpClient
@using System.Net.Http.Headers

<InputFile OnChange="@HandleSelected" multiple />
@if (ImgUrls is not null)
{
    @foreach (var url in ImgUrls)
    {
        <div>
            <img src="@url" class="image-preview" style="width: 300px" />
        </div>
    }
}

@code{
    public IEnumerable<string>? ImgUrls { get; set; }

    private async Task HandleSelected(InputFileChangeEventArgs e)
    {
        long maxFileSize = 1024 * 1024 * 15;
        using (var content = new MultipartFormDataContent())
        {
            content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
            foreach (var imageFile in e.GetMultipleFiles())
            {
                var fileContent = new StreamContent(imageFile.OpenReadStream(maxFileSize));
                content.Add(fileContent, "images", imageFile.Name);
            }
            var response = await HttpClient.PostAsync("api/UploadAzure/Upload", content);
            ImgUrls = await response.Content.ReadFromJsonAsync<IEnumerable<string>?>();
        }
    }
}

ファイルの選択コンポーネントのみのシンプルな画面で、アップロードが完了したら画像を画面に表示します。

ファイルの最大サイズは 15 MB です。

InputFile コンポーネントに multiple を指定することで、複数ファイルを一度にアップロードできるので、GetMultipleFiles ですべてのファイルを取得して Form データに追加しています。

Form に詰め込んだデータは、Controller で FromForm の属性をつけることで引数で受け取ることができます。

動作確認

動作確認をしていきましょう。

File Upload のメニューを用意しましたが、直接 URL を指定しても問題ありません。

azure-blob-storage-upload

ファイルは複数指定できるようにしているので、今回は2つ指定してアップロードしました。

正常にアップロードできた場合は、アップロード先の URL が返却され、画面に画像が表示されます。

azure-blob-storage-upload

Azure のコンテナーを見てみましょう。

プログラムで指定した「upload-container」という名前で作成され、ファイルの数だけフォルダができていることが確認できます。

azure-blob-storage-upload

フォルダの中を見ると、ファイル名がタイムスタンプになっている画像が入っていますね。

azure-blob-storage-upload

最後に

Azure Blob Storageに画像をアップロードする手順を解説しました。

ファイルのアップロードは少し複雑なので、専用のコンポーネントを採用したほうが実装が楽になるかもしれません。

本記事を参考に、作成中のアプリにファイルのアップロード機能を組み込んでみてください。

Blazor の書籍も好評発売中!
blazor-book

入門編から EC サイトを作る応用編まで、Blazor の本を3冊執筆しました。

私が1年以上かけて学習した内容をすべて詰め込んでいるので、さらにステップアップしたい方はぜひご覧ください。