by WebSurfer
2. April 2023 20:46
JavaScript を使って、ボタンクリックでファイル選択ダイアログを表示し、ユーザーがダイアログに表示されたファイルを選択したら即アップロードする方法を書きます。
ファイルのアップロードでよくあるパターンは、<form> 要素の中に <input type="file"> 要素と <input type="submit"> 要素を静的に配置しておいて、(1) <input type="file"> のファイル選択ボタンをクリックしてファイル選択ダイアログを表示、(2) ユーザーがダイアログからファイルを選択、(3) <input type="submit"> をクリックして選択されたファイルを POST 送信する・・・というものだと思います。
その (1) から (3) のステップを短縮し、<input type="button"> ボタンをクリックしたらファイル選択ダイアログを表示し、ユーザーがファイルを選択したら File API を利用して File オブジェクトを取得し、それを fetch API を使って即 Web API にアップロードしてみます。
クライアント側のコードは以下の通りです。<input type="button"> ボタンだけを静的に配置しておき、それ以外は JavaScript で処置しています。説明はコード内のコメントに書きましたのでそれを見てください。
コードは ASP.NET Core MVC の View をものをそのままコピー&ペーストしましたが、その中の html と JavaScript のコードはどのような Web アプリにも使えるはずです。
@{
ViewData["Title"] = "UploadFile";
}
<h1>UploadFile</h1>
<button type="button" class="btn btn-primary" id="btn">
ファイル選択してアップロード
</button>
<div id="result"></div>
@section Scripts {
<script type="text/javascript">
window.addEventListener('DOMContentLoaded', () => {
// Promise を返す非同期メソッド
const showOpenFileDialog = async () => {
return new Promise(resolve => {
// input type="file" 要素を動的に生成、
const input = document.createElement('input');
input.type = 'file';
// .jpg ファイルのみ選択できるよう設定してみた
input.accept = '.jpg';
// ユーザーがファイルを選択すると change イベント
// が発生するのでそれにリスナをアタッチし、File
// オブジェクトを取得。それを Promise の resolve
// コールバックに設定
input.addEventListener('change',
e => resolve(e.target.files[0]));
// クリックしてファイル選択ダイアログを開く
input.click();
});
};
// ボタンクリックで上のメソッドを呼び出し File オブジェ
// クトを取得。それを fetch API を使って Web API に送信
document.getElementById("btn")
.addEventListener('click', async () => {
const file = await showOpenFileDialog();
const formData = new FormData();
formData.append("postedFile", file);
const param = {
method: "POST",
body: formData
}
const response = await fetch("/api/Upload", param);
const data = await response.text();
document.getElementById("result").innerText = data;
});
});
</script>
}
サーバー側で、アップロードされてきたファイルを受けて保存する ASP.NET Core Web API のコードも参考までに以下に載せておきます。
using Microsoft.AspNetCore.Mvc;
namespace MvcCore6App3.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UploadController : ControllerBase
{
// 物理パスの取得用
private readonly IWebHostEnvironment _hostingEnvironment;
public UploadController(IWebHostEnvironment hostingEnvironment)
{
this._hostingEnvironment = hostingEnvironment;
}
// .NET 6.0 で作ったプロジェクトは「Null 許容」オプションが「有効化」
// に設定してあるので、引数も null 許容にしておかないと、クライアント
// でファイルを選択しないで送信した場合、HTTP 400 Bad Request エラー
// になって "The postedFile field is required."というエラーメッセージ
// が返ってくる
[HttpPost]
public async Task<string> Post([FromForm] IFormFile? postedFile)
{
string result = "";
if (postedFile != null && postedFile.Length > 0)
{
// アップロードされたファイル名を取得
string filename = System.IO.Path.GetFileName(postedFile.FileName);
// アプリケーションルートの物理パスを取得
string contentRootPath = _hostingEnvironment.ContentRootPath;
string filePath = $"{contentRootPath}\\UploadedFiles\\" +
$"{filename}{DateTime.Now.ToString("yyyyMMddHHmmss")}.jpg";
// フォルダ UploadedFile に画像ファイルを保存
using (var stream = new FileStream(filePath, FileMode.Create))
{
await postedFile.CopyToAsync(stream);
}
result = $"{filename} ({postedFile.ContentType}) - " +
$"{postedFile.Length} bytes アップロード完了";
}
else
{
result = "ファイルアップロードに失敗しました";
}
return result;
}
}
}