WebSurfer's Home

Filter by APML

Web API に Blob をアップロード

by WebSurfer 4. February 2025 12:28

ASP.NET Core Web API で、ブラウザから fetch や axios を使って multipart/form-data 形式でアップロードされてきた Blob データをどのように取得できるかということを書きます。

axios のドキュメント Multipart Bodies のサンプルコード(抜粋下記)を見て、そこに書いてある Blob データを Web API でどのように取得できるかを考えたのがきっかけです。

const form = new FormData();
form.append('my_field', 'my value');
form.append('my_buffer', new Blob([1,2,3]));
form.append('my_file', fileInput.files[0]);

上のコードの FormData を、ブラウザから JavaScript の fetch や axios を使って multipart/form-data 形式で送信すると、ボディ部分は下のようになります (Fiddler によるキャプチャ画像)。2 つ目の name が my_buffer となっているパートが Blob データです。

Fiddler によるキャプチャ画像

上のように送信されてきた my_buffer の Blob データは、ASP.NET Core Web API のアクションメソッドの引数の型を IFormFile 型とすれば取得できました。

具体的には、Web API プロジェクトで以下のクラスを定義し、

public class Blob
{
    public string? My_field { get; set; }
    public IFormFile? My_buffer { get; set; }
    public IFormFile? My_file { get; set; }
}

それをアクションメソッドの引数に設定して、JavaScript の fetch や axios を使って FormData をアクションメソッドに POST 送信すれば、下の画像の通り My_buffer プロパティに Blob を取得できます。

My_buffer プロパティに Blob を取得

MDN のドキュメント Blob に、

"File インターフェイスは Blob をベースにしており、 Blob の機能を継承してユーザーのシステム上のファイルをサポートするように拡張しています"

・・・と書いてあるとおり、input type="file" を使ってのファイルと同様の扱いになるということのようです。

最初、バイト配列として取得できるのではと思って、上の Blob クラスの My_buffer プロパティの型を byte[]? として試してみたのですが、バインドできないようで null になってしまいます。

My_buffer プロパティが byte[]? 型の場合

以下に検証に使ったコードを載せておきます。Visual Studio 2022 のテンプレートを使ってターゲットフレームワーク .NET 9.0 で作成した ASP.NET Core Web API アプリです。

Controller

using Microsoft.AspNetCore.Mvc;
using WebApi.Models;

namespace WebApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UploadController : ControllerBase
    {
        [HttpPost("blob")]
        public async Task<IActionResult> ReceiveBlob([FromForm] Blob model)
        {
            string result = $"My_field: {model.My_field}";

            if (model.My_file != null && model.My_file.Length > 0)
            {
                string filename = Path.GetFileName(model.My_file.FileName);
                result += $", My_file: {filename}";
            }

            if (model.My_buffer != null && model.My_buffer.Length > 0)
            {
                string array = string.Empty;
                using (var stream = new MemoryStream())
                {
                    await model.My_buffer.CopyToAsync(stream);
                    byte[] bytes = stream.ToArray();
                    foreach (byte b in bytes)
                    {
                        array += $"[{b:x2}]";
                    }
                }
                result += $", My_buffer: {array}";
            }

            return Content(result);
        }
    }
}

View

@{
    ViewData["Title"] = "SendBlob";
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewData["Title"] - WebApi</title>
    <script type="text/javascript">
        const url = "/api/upload/blob";

        const upload = async () => {
            const fileInput = document.getElementById("fileupload");
            const resultDiv = document.getElementById("result");
            const form = new FormData();
            form.append('my_field', 'my value');
            form.append('my_buffer', new Blob([1, 2, 3]));
            form.append('my_file', fileInput.files[0]);

            const param = {
                method: "POST",
                body: form
            }
            const response = await fetch(url, param);
            if (response.ok) {
                const message = await response.text();
                resultDiv.innerText = message;
            } else {
                resultDiv.innerText = "アップロード失敗";
            }
        };

        window.addEventListener('DOMContentLoaded', () => {
            const btn = document.getElementById("button1");
            btn.addEventListener("click", upload);
        });
    </script>
</head>
<body>
    <input type="file" name="fileupload" id="fileupload" multiple="multiple" />
    <br />
    <button type="button" id="button1">Upload</button>
    <br />
    <div id="result"></div>
</body>
</html>

上の View のコードの実行結果は以下のようになります。

View のコードの実行結果

(メモ: プロジェクトは VS2022 AspNet9 WebApi)

Tags: , , ,

Upload Download

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  February 2025  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
242526272812
3456789

View posts in large calendar