ASP.NET Core アプリケーションで Razor Pages を使用してファイルをアップロードする

ASP.NET Core アプリケーションの Razor Pages でファイルをアップロードするコードを紹介します。

概要

Razor Pages でファイルをアップロードする場合はinputタグのファイル選択コントロールを利用します。ページモデルで参照できるようにするため、 タグヘルパーを利用し、asp-for 属性を指定してページモデルにバインドします。

プログラム

ASP.NET Core Webアプリケーションを作成します。 ターゲットフレームワークは.NET 5を利用しています。

コード

以下のコードを記述します。
startup.csにはRazor Pagesの基本的な設定を記述します。ConfigureServices メソッドにAddRazorPages()メソッドの記述、 UseEndpoints メソッド内にページマップのMapRazorPages()メソッドを記述します。 詳しくはこちらの記事を参照してください。
startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace FileUpload
{
  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();
    }

    // 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.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapRazorPages();
      });
    }
  }
}

Pagesフォルダを作成し、Razorページのファイルを配置します。 simple-upload.cshtml がファイルをアップロードするページになります。
input タグを配置し、type="file"を指定し、ファイル選択コントロールとします。また、asp-for 属性に ページモデルクラスの "FileData" プロパティを指定します。
simple-upload.cshtml
@page
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model FileUpload.Pages.simple_uploadModel
@{
}
<html>
<head>

</head>
<body>
  <form method="post" enctype="multipart/form-data">
    <div>ファイルのアップロード<input type="file" asp-for="FileData" /></div>
    <input type="submit" value="POST" />
  </form>
</body>
</html>

simple-upload.html.cs は simple-upload.cshtml のページモデルクラスです。FileData プロパティを宣言します。プロパティにはBindProperty 属性を付与します。 BindProperty属性についてはこちらの記事もを参照してください。
simple-upload.html.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Http;

namespace FileUpload.Pages
{
  public class simple_uploadModel : PageModel
  {

    [BindProperty]
    public IFormFile FileData { get; set; }

    public void OnGet()
    {
    }

    public IActionResult OnPost()
    {
      System.IO.Stream stream = FileData.OpenReadStream();
      byte[] buffer = new byte[stream.Length];
      stream.Read(buffer, 0, (int)stream.Length);

      System.IO.FileStream fs = new System.IO.FileStream(FileData.FileName, System.IO.FileMode.CreateNew);
      fs.Write(buffer, 0, buffer.Length);
      fs.Close();
      stream.Close();

      return RedirectToPage("/simple-upload-complete");
    }
  }
}

simple-upload-complete.cshtml はアップロード完了時に表示されるページです。 ページ内にアップロード完了のメッセージを表示します。
simple-upload-complete.cshtml
@page
@model FileUpload.Pages.aaaModel
@{
}
<html>
<head>

</head>
<body>
  <p>ファイルをアップロードしました。</p>
</body>
</html>

simple-upload-complete.html.cs は simple-upload-complete.htmlのページモデルです。標準で生成されたコードから変更はありません。 simple-upload-complete.cshtmlの@model参照を削除し、simple-upload-complete.html.csを削除してページモデルを使用しない状態にしても問題ありません。
simple-upload-complete.html.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace FileUpload.Pages
{
    public class aaaModel : PageModel
    {
        public void OnGet()
        {
        }
    }
}

ファイルの配置

ファイルの配置は下図の通りです。

解説

simple-upload.cshtml

formタグ内にinputコントロールを配置します。formタグのmethod属性は post に設定し、ポストでフォームを送信します。
また、バイナリファイルを送信するため、enctype 属性に "multipart/form-data" を指定します。
inputタグは type 属性に "file" を指定しファイル選択コントロールとします。 asp-for 属性にはページモデルクラスのFileDataプロパティを設定します。
  <form method="post" enctype="multipart/form-data">
    <div>ファイルのアップロード<input type="file" asp-for="FileData" /></div>
    <input type="submit" value="POST" />
  </form>

simple-upload.cshtml.cs

simple-upload.cshtml のページモデルクラスでは、クラスにFileData プロパティを宣言します。
ページから参照できるようにするため、[BindProperty]属性を付与します。
    [BindProperty]
    public IFormFile FileData { get; set; }

フォームからポスト処理された際にページモデルクラスで実行されるコードが以下のOnPostメソッドになります。
アップロードされたファイルを読み取るため、FileDataプロパティのOpenReadStream() メソッドを呼び出します。アップロードされたファイルを読み取るストリームが取得できます。 ストリームのReadメソッドを呼び出し、ファイルのバイナリ情報をbyte配列に読み取ります。

アップロードされたファイルをサーバーのディスク上に配置します。今回はコードをシンプルにするため、アプリケーションのカレントディレクトリに 配置するコードとしています。ファイルを書き込むため、FileStereamクラスを作成します。コンストラクタの第一引数にファイル名を与えます。 アップロードされたファイル名がFileData.FileName プロパティに代入されているため、同じ名前で保存します。台に引数にはファイルを新規作成する FileMode.CreateNew フラグを与えます。
ファイルストリームのWriteメソッドを呼び出し、byte配列の情報をファイルに書き込みます。

処理の完了後にリダイレクト処理を実行し、アップロード完了メッセージのあるぺージを表示します。

    public IActionResult OnPost()
    {
      System.IO.Stream stream = FileData.OpenReadStream();
      byte[] buffer = new byte[stream.Length];
      stream.Read(buffer, 0, (int)stream.Length);

      System.IO.FileStream fs = new System.IO.FileStream(FileData.FileName, System.IO.FileMode.CreateNew);
      fs.Write(buffer, 0, buffer.Length);
      fs.Close();
      stream.Close();

      return RedirectToPage("/simple-upload-complete");
    }

実行結果

プロジェクトを実行します。Webブラウザが起動しますので、(Webアプリケーションのルート)/simple-upload のURLにアクセスします。 下図のページが表示されます。ページの[ファイルの選択]ボタンをクリックします。


ファイル選択ダイアログボックスが表示されます。アップロードしたいファイルをクリックして選択し、[開く]ボタンをクリックします。


ファイルが選択された状態になります。[ファイルの選択]ボタンの右側に選択されたファイル名が表示されます。
ファイル選択後[POST]ボタンをクリックします。


ファイルのアップロードが実行され、simple-upload-complete ページにリダイレクトされます。ファイルがアップロードされた旨のメッセージが表示されます。


ファイルがアップロードされているか確認します。ASP.NET Webアプリケーションのフォルダをエクスプローラで開きます。先ほどアップロードした、 image.png ファイルが配置されていることが確認できます。


image.pngファイルを開きます。ファイルの内容も問題ないです。

補足

ファイルを保存するためのFileStreamの作成時に FileMode.CreateNew を指定しているため、同じファイル名のファイルを再アップロードすると、 既にファイルが存在している旨の例外が発生します。回避するには、FileMode.Create を指定して上書きを許可するモードでFileStreamを作成するか、ファイルの重複判定をして 同じファイルで保存されない仕組みを実装する必要があります。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2021-05-02
作成日: 2020-12-28
iPentec all rights reserverd.