ほとんどのWebサイトでは、画像のアップロードも重要な機能です。今回はそれを操作してみましょう。
(注:これは Blazor Server の方法ですが、あまり多くのファイルをアップロードしないほうが良いです。そのため、アップロードを4枚に制限すると警告が表示されます。これらの処理はすべてサーバー上で行われるため負荷が大きくなり、Microsoft も .NET Core Web API の方法を推奨しています。)
まず、コンポーネント FileUpload を作成します。
以下のコードは FileUpload.razor です。Blazor が提供するコンポーネント <InputFile> を使用しています。multiple は複数のファイルをアップロードできることを意味します。
@page "/FileUpload"
<div>
<div>
<InputFile OnChange="OnChange" multiple></InputFile>
</div>
<div>
<MyButton value="Submit" class="btn btn-primary" type="submit" @onclick="OnSubmit"></MyButton>
</div>
</div>
@if (ImageList.Count > 0)
{
<table>
<tr>
@foreach (var img in ImageList)
{
<td>
<img src="@img" width="150" height="150"/>
</td>
}
</tr>
</table>
}
以下のコードは FileUpload.razor.cs です。ここでは partial クラスを使用しています。
using BlazorServer.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.JSInterop;
using System.Text.Json;
namespace BlazorServer.Shared;
public partial class FileUpload
{
private JsInteropClasses? _jsClass;
// `<InputFile>` のファイル内容を取得
public IReadOnlyList<IBrowserFile>? ImageFiles;
public List<string> ImageList = new();
public string? ImageSrc;
[Inject] protected IJSRuntime Js { get; set; }
/// <summary>
/// ランタイム時の実行環境を判断するために使用
/// </summary>
[Inject]
protected IWebHostEnvironment? Env { get; set; }
protected override Task OnInitializedAsync()
{
_jsClass = new JsInteropClasses(Js);
return base.OnInitializedAsync();
}
public async Task OnChange(InputFileChangeEventArgs e)
{
ImageList = new List<string>();
const string format = "image/jpeg";
// ファイルを取得
ImageFiles = e.GetMultipleFiles();
foreach (var file in ImageFiles)
{
// 画像コンテンツを指定の形式と最大サイズに変換
var imageFile = await file.RequestImageFileAsync(format, 1200, 675);
// Stream を使用して画像コンテンツを読み込む
await using var fileStream = imageFile.OpenReadStream();
// Stream をメモリに読み込む(アップロード確定前はメモリを無駄にしないため推奨しない)
await using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
ImageSrc = $"data:{format};base64,{Convert.ToBase64String(memoryStream.ToArray())}";
// Data URI の形式で画像を表示
ImageList.Add(ImageSrc);
}
}
public async Task OnSubmit()
{
// 確認メッセージを ViewModel に変換
var sweetConfirm = new SweetConfirmViewModel
{
RequestTitle = "画像をアップロードしてもよろしいですか?",
ResponseTitle = "アップロード成功"
};
var jsonString = JsonSerializer.Serialize(sweetConfirm);
var result = await _jsClass!.Confirm(jsonString);
if (result && ImageFiles != null && ImageFiles.Any())
{
long maxFileSize = 1024 * 1024 * 15;
// 画像保存先のパスを指定
var folder = $@"{Env!.WebRootPath}\images";
foreach (var file in ImageFiles)
{
// Stream を使用してファイルを指定パスに保存
await using var stream = file.OpenReadStream(maxFileSize);
// フォルダが存在しない場合は作成
Directory.CreateDirectory(folder);
var path = $@"{Env.WebRootPath}\images\{file.Name}";
// ファイルを作成
var fs = File.Create(path);
// 画像の Stream をファイルにコピー
await stream.CopyToAsync(fs);
// Stream は使い終わったら閉じる
stream.Close();
fs.Close();
}
}
}
}
便宜上、NavMenu.razor にこのコンポーネントに遷移するルートを追加します。
<div class="nav-item px-3">
<NavLink class="nav-link" href="FileUpload" Match="NavLinkMatch.All">
<span class="bi bi-card-image h4 p-2 mb-0" aria-hidden="true"></span> File
Upload
</NavLink>
</div>
新しい ViewModel を作成し、SweetConfirm を汎用的に使用できるようにします。
namespace BlazorServer.ViewModels;
public class SweetConfirmViewModel
{
public string? ResponseTitle { get; set; }
public string? ResponseText { get; set; }
public string? RequestTitle { get; set; }
public string? RequestText { get; set; }
}
さらに、_Layout.cshtml の SweetConfirm を修正します。
function SweetConfirm(jsonString) {
// ここで parse して正しく戻り値を渡す必要がある
var arg = JSON.parse(jsonString);
return new Promise((resolve) => {
swal({
title: arg.RequestTitle,
text: arg.RequestText,
icon: "warning",
buttons: ["キャンセル", "確定"],
dangerMode: true
}).then((willDelete) => {
resolve(willDelete);
if (willDelete) {
swal(arg.ResponseTitle, arg.ResponseText, "success");
}
});
});
}
こちらも修正したので、PostBase.razor.cs の DeletePost も変更します。
protected async Task DeletePost()
{
// ViewModel に変更
var sweetConfirm = new SweetConfirmViewModel
{
RequestTitle = $"ログ {Post!.Title} を削除してもよろしいですか?",
RequestText = "この操作は元に戻せません",
ResponseTitle = "削除成功",
ResponseText = "ログが削除されました"
};
var jsonString = JsonSerializer.Serialize(sweetConfirm);
var result = await _jsClass.Confirm(jsonString);
if (result)
{
var deleted = await PostRepository!.DeletePost(Post.Id);
if (deleted.IsSuccess)
await GetPostId.InvokeAsync(Post!.Id);
else
await _jsClass.Alert(deleted.Message!);
}
}
JsInteropClasses.cs の Confirm() を JSON 文字列を受け取るように変更します。
public async ValueTask<bool> Confirm(string jsonString)
{
var confirm = await _js.InvokeAsync<object?>("SweetConfirm", jsonString);
if (confirm == null)
return false;
return bool.TryParse(confirm.ToString(), out var result) && result;
}
画像のアップロードが成功したことが確認できます。

参照:
- ASP.NET Core Blazor file uploads
- Upload Files Using InputFile Component In Blazor
- What scope does a using statement have without curly braces
- BrowserFileExtensions.RequestImageFileAsync(IBrowserFile, String, Int32, Int32) メソッド
- Day 26:Blazor WebAssembly アップロードファイル
注:本記事のコードは .NET 6 + Visual Studio 2022 でリファクタリングされています。詳細は原文のリンクとリファクタリング後のコードを比較してご確認ください。ご覧いただきありがとうございます。原作者をサポートしてください。