ASP.NET Core MVCアプリから画像ファイルをアップロードして、Azure Blob Storageにファイルを保存する方法について解説します。
※Azure Blob Storageとは
テキストファイルやバイナリデータといった非構造化データを保存できる、Azureのオブジェクトストレージサービスです。
Blob Storageには3種類のリソースがあり、以下のような関係になっています。
- ストレージアカウント:Azureストレージサービスを使用するためのアカウント
- コンテナ:フォルダのようなもので、ストレージアカウント直下に直接ファイルを格納できないため疑似的に作成する
- BLOB:コンテナ内に格納されるファイル
今回はAzureポータルからストレージアカウントを作成した後、C#のコードからコンテナを作成し、その中のBLOBにアップロードした画像ファイルを保存してみます。
環境
- Visual Studio 2022
- .NET 8
前提
- Azureアカウントが作成済みであること
ストレージアカウントの作成
まずストレージアカウントの作成方法について説明します。
既に作成済みの場合は飛ばしてください。
ストレージアカウントの作成
Azureポータルを開いたら、メニューから「ストレージアカウント」を選択します。
その画面内にある「作成」をクリックすると、以下のストレージアカウント作成画面が開きます。
主な設定項目は以下の通りです。費用がかからないようにするため、オプションはレベルを落としたものを選択しています。
- リソースグループ:任意の名前で新規作成します。既存のリソースグループがある場合はそれを選択してもOKです。
- ストレージアカウント名:任意の名前を設定します。英子文字と数字しか使えません。
- 地域:日本国内の場合は一般的に「Japan East」か「Japan West」を選択します。
- パフォーマンス:サンプル用なのでStandardを選択します。
- 冗長性:サンプル用なので一番冗長性が低い「ローカル冗長ストレージ」を選択します。
その他の設定はデフォルトのもので大丈夫です。
設定できたら、「レビュー」画面からストレージアカウントを作成しましょう。
以下のように表示されたら作成完了です。リソースに移動します。
接続文字列の確認
ストレージアカウントの左メニューにある「アクセスキー」を開くと、ストレージに必要な接続文字列を確認することができます。接続文字列は後ほど使用します。
サンプルコード
それではASP.NET Core MVCのサンプルコードを用いて解説します。
画像を1つ選択してアップロードボタンをクリックすると、その画像がAzure Blob Storage内に保存されるというシンプルなものです。
準備
Visual StudioでASP.NET Core MVC(.NET 8)のプロジェクトを作成したら、まず先ほど確認した接続文字列(key1)をコピーして、appsettings.jsonファイル内に記載します。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"StorageAccount": "<コピーした接続文字列>"
}
}
次に、Blob Storageへの接続に必要なライブラリ「Azure.Storage.Blobs」をNuGet Package Managerなどからプロジェクトにインストールします。
以上でストレージアカウントに接続する準備は完了です。
ビュー
まずはビューです。index.cshtmlに以下のコードを記述します。
@model UploadModel
@{
ViewData["Title"] = "ファイルアップロード";
}
<h1>画像ファイルアップロード</h1>
@if (ViewData["Result"] != null)
{
@ViewData["Result"];
}
<form asp-action="Index" enctype="multipart/form-data" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="mb-3">
<input asp-for=File class="form-control" type="file">
</div>
<div class="form-group">
<input class="btn btn-primary" type="submit" value="アップロード" />
</div>
</form>
ファイルを選択するためのinputフォームとアップロードボタン、コントローラー側からの結果メッセージを表示する欄などで構成されています。
ポイントは、フォーム送信時のenctypeに"multipart/form-data"
を指定している点です。これを指定しないとコントローラー側で受け取る際にnullになってしまいます。
モデル
次に、アップロードファイル用のモデルと、アップロードされたファイルを検証するためのカスタム検証属性「UploadFile」を用意します。
using System.ComponentModel.DataAnnotations;
namespace BlobStorageSample.Models
{
/// <summary>
/// アップロードファイル用モデル
/// ※アップロードされたファイルはIFormFile型として送信される
/// </summary>
public class UploadModel
{
[UploadFile]
public IFormFile? File { get; set; }
}
/// <summary>
/// アップロードファイル検証用のカスタム属性
/// </summary>
public class UploadFileAttribute : ValidationAttribute
{
// 許可するファイルサイズ上限(今回は2MBを指定)
private readonly long fileSizeLimit = 2097152;
// 許可する拡張子
private readonly string[] permittedExtensions = [".jpg", ".jpeg", ".png", ".gif"];
// 検証メソッド
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
// UploadModel型に変換
var upload = validationContext.ObjectInstance as UploadModel;
// ファイルが存在しない場合はエラー
if (upload?.File == null)
{
return new ValidationResult("ファイルを選択してください。");
}
// ファイルサイズ上限より大きい場合はエラー
if (upload.File.Length > fileSizeLimit)
{
return new ValidationResult("ファイルサイズが上限を超えています。");
}
// 小文字に変換したファイルの拡張子を取得
var extension = Path.GetExtension(upload.File.FileName).ToLowerInvariant();
// 画像の拡張子でない場合はエラー
if (string.IsNullOrEmpty(extension) || !permittedExtensions.Contains(extension))
{
return new ValidationResult("画像ファイルを選択してください。");
}
return ValidationResult.Success;
}
}
}
なんでもアップロードできてしまうのはセキュリティ上よろしくないため、下記ドキュメントを参考にファイルサイズや拡張子の検証を行っています。
コントローラー
最後に、HomeControllerにアップロードされたファイルをBlob Storageに保存する処理を記述します。
using Azure.Storage.Blobs;
using BlobStorageSample.Models;
using Microsoft.AspNetCore.Mvc;
namespace BlobStorageSample.Controllers
{
[AutoValidateAntiforgeryToken]
public class HomeController : Controller
{
private readonly string _connectionString = string.Empty;
// 設定ファイルからストレージアカウントの接続文字列を取得
public HomeController(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("StorageAccount")
?? throw new ArgumentNullException("接続文字列が指定されていません。");
}
/// <summary>
/// 画面表示
/// </summary>
public IActionResult Index() => View();
/// <summary>
/// 画像ファイルがアップロードされた場合はBlob Storageに保存する
/// </summary>
/// <param name="upload">フォームからアップロードされたファイル</param>
[HttpPost]
public async Task<IActionResult> Index([FromForm] UploadModel upload)
{
if (!ModelState.IsValid)
{
return View(upload);
}
// Blob Storageのコンテナ名(確実に一意にするためにGUIDを連結)
var containerName = $"sample{Guid.NewGuid()}";
try
{
// BlobServiceClientクラスのインスタンスを作成
var blobServiceClient = new BlobServiceClient(_connectionString);
// コンテナを作成する
BlobContainerClient containerClient = await blobServiceClient.CreateBlobContainerAsync(containerName);
// BlobClientクラスのインスタンスを取得
var blobClient = containerClient.GetBlobClient(upload.File.FileName);
// ファイルをBLOBにアップロードする
await blobClient.UploadAsync(upload.File.OpenReadStream());
ViewData["Result"] = "ファイルをアップロードしました!";
}
catch
{
ViewData["Result"] = "ファイルのアップロードに失敗しました。";
}
return View();
}
}
}
Blob Storageへの接続からファイルのアップロードまでの流れを簡単に説明します。
- 接続文字列からBLOBコンテナを操作するための
BlobServiceClient
クラスのインスタンスを生成する BlobServiceClient
のCreateBlobContainerAsync
メソッドでコンテナを作成する
※同じ名前のコンテナが既に存在する場合は例外がスローされる- 戻り値の
BlobContainerClient
クラスのGetBlobClient
メソッドで、BlobClient
クラスのインスタンスを生成する BlobClient
クラスのUploadAsync
メソッドで、ファイルをアップロードする
※同じ名前のファイルが既に存在する場合は例外がスローされる
※第2引数のoverwriteをtrueに指定すると、既に存在する場合はファイルが上書きされる
サンプルコードの実行
適当な画像を選択してアップロードボタンをクリックします。
「ファイルをアップロードしました!」と表示されれば処理成功です。
Azureポータルにログインしてストレージアカウントのコンテナーを見てみると、アップロードしたファイルが格納されていることが確認できます。
以上、画像ファイルをアップロードしてAzure Blob Storageに保存するまでを確認しました。
参考ドキュメント
․NET CoreからAzure Blob Storageにファイルをアップロードする方法について
カスタム検証属性について