Blazor アプリケーションでファイルをアップロードする - BlazorInputFileを利用

Blazor アプリケーションでファイルをアップロードするコードを紹介します。

概要

Blazorアプリケーションで画像などのファイルをサーバーにアップロードしたい場合があります。Blazorアプリケーションの仕組みでは、ファイルを簡単にアップロードする仕組みは無いため、ファイルアップロードのライブラリを利用して実装するのが容易です。ファイルアップロードのライブラリはいくつかありますが、この記事では BlazorInputFile ライブラリを利用してファイルをアップロードする方法を紹介します。

BlazorInputFile のインストール

Visual Studioを起動し、ASP.NET Core のプロジェクトを作成します。

GUIのNuGetパッケージマネージャー、または、NuGetパッケージマネージャーを開き、BlazorInputFile パッケージをインストールします。

手順は以下の記事を参照してください。
  • GUIのNuGetパッケージマネージャーでインストールする手順はこちらの記事
  • NuGetパッケージマネージャーコンソールでインストールする手順はこちらの記事
パッケージマネージャーコンソールの場合は以下のコマンドを実行します。
NuGet\Install-Package BlazorInputFile -Version 0.2.0

プログラム例

コード

以下のコードを準備します。
App.razor
<Router AppAssembly="@typeof(Program).Assembly">
  <Found Context="routeData">
    <RouteView RouteData="@routeData" />
  </Found>
  <NotFound>
    <LayoutView>
      <p>Sorry, there's nothing at this address.</p>
    </LayoutView>
  </NotFound>
</Router>
_Imports.razor
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.JSInterop
@using BlazorInputFile
Srartup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace FileUpdload
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddRazorPages();
      services.AddServerSideBlazor();
      services.AddScoped<IFileUploadService, FileUploadService>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.UseStaticFiles();
      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapBlazorHub();
        endpoints.MapFallbackToPage("/_Host");
      });
    }
  }
}

Program.csは変更しません。

FileUploadService.cs の名称でC#のクラスファイルを新規作成します。下記のコードを記述します。
FileUploadService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using BlazorInputFile;
using System.IO;

namespace FileUpdload
{
  public interface IFileUploadService
  {
    Task UploadAsync(IFileListEntry file);
  }

  public class FileUploadService : IFileUploadService
  {
    private readonly IWebHostEnvironment _environment;

    public FileUploadService(IWebHostEnvironment env)
    {
      _environment = env;
    }

    public async Task UploadAsync(IFileListEntry fileEntry)
    {
      string path = Path.Combine(_environment.ContentRootPath, "Upload", fileEntry.Name);
      MemoryStream ms = new MemoryStream();
      await fileEntry.Data.CopyToAsync(ms);
      FileStream file = new FileStream(path, FileMode.Create, FileAccess.Write);
      ms.WriteTo(file);
      file.Close();
      ms.Close();
    }
  }
}
Pagesフォルダを作成し、_Host.cshtml の名称でrazorページを追加します。
_Host.cshtml
@page
@model FileUpdload.Pages._HostModel
@namespace FileUpdload.Pages
<!DOCTYPE html>
<html lang="ja">
<head>
  <script src="_content/BlazorInputFile/inputfile.js"></script>
</head>
<body>
  <app>
    @(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
  </app>
  <script src="_framework/blazor.server.js"></script>
</body>
</html>

@{
}

Pages フォルダ内に Index.razor の名称でrazorコンポーネントを作成します。
Index.razor
@page "/Index"
@inject IFileUploadService fileUploadService
<h3>FileUpload</h3>
<p></p>

<BlazorInputFile.InputFile OnChange="HandleFileSelected" />

@code {
  async Task HandleFileSelected(IFileListEntry[] files)
  {
    IFileListEntry file = files.FirstOrDefault();
    if (file != null) {
      await fileUploadService.UploadAsync(file);
    }
  }
}

ファイルの一覧や配置場所は下図となります。

解説

ファイルのアップロードイベント側から、IWebHostEnvironment オブジェクトを参照したいため、アップロードの処理は、FileUploadService クラスとして実装しています。このクラスをDIコンテナに登録して、インジェクションを利用します。DIコンテナを利用するため、FileUploadService クラスのインターフェイスオブジェクトとなる IFileUploadService も実装しています。DIコンテナの利用とインジェクションの詳細はこちらの記事を参照して下さい。

DIコンテナへの登録は、Startup.csのConfigureServicesメソッドの、AddScopedメソッドで登録します。
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddRazorPages();
      services.AddServerSideBlazor();
      services.AddScoped<IFileUploadService, FileUploadService>();
    }

FileUploadService のコンストラクタにIWebHostEnvironment が与えられるので、このオブジェクトをメンバ変数に保持してアップロードイベントの処理時に参照できるようにします。
  public class FileUploadService : IFileUploadService
  {
    private readonly IWebHostEnvironment _environment;

    public FileUploadService(IWebHostEnvironment env)
    {
      _environment = env;
    }
    //....(中略)
  }

"/Index" URLにアクセスすると Index.razor ファイルのページが表示されます。InputFile タグがBlazorInputFileのファイルアップロードコンポーネントになります。アップロードコンポーネントの状態に変化が起きると、HandleFileSelected メソッドを呼び出します。
HandleFileSelected メソッドでは、アップロードのファイルの IFileListEntry[] の最初のファイルを確認します。ファイルが選択されていれば、FileUploadServiceクラスのUploadAsyncメソッドを呼び出し。ファイルのアップロードを実行します。
<InputFile OnChange="HandleFileSelected" />

@code {
  async Task HandleFileSelected(IFileListEntry[] files)
  {
    IFileListEntry file = files.FirstOrDefault();
    if (file != null) {
      await fileUploadService.UploadAsync(file);
    }
  }
}

UploadAsyncメソッドでは、サーバーに保存するファイル名を作成します。アプリケーションが存在する物理パスを WebHostEnvironment.ContentRootPath プロパティで取得します。ルートディレクトリ下の"Upload" フォルダにアップロードされたファイル名と同じ名前のファイルとして保存するパスを作成します。作成したパスは path 変数に保存されます。
続いて、メモリストリームを作成し、アップロードされたファイルをCopyToAsyncメソッドにより、メモリストリームにコピーします。
その後ファイルストリームを作成し、メモリーストリームの内容を先に作成したpathのファイルに保存します。
  public async Task UploadAsync(IFileListEntry fileEntry)
  {
    string path = Path.Combine(_environment.ContentRootPath, "Upload", fileEntry.Name);
    MemoryStream ms = new MemoryStream();
    await fileEntry.Data.CopyToAsync(ms);
    FileStream file = new FileStream(path, FileMode.Create, FileAccess.Write);
    ms.WriteTo(file);
    file.Close();
    ms.Close();
  }

参考ページ

https://thecodehubs.com/file-uploads-with-blazor/
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2023-11-19
作成日: 2019-12-23
iPentec all rights reserverd.