あきちゃんの飽き飽き備忘録

ほぼ死んでるブログ

Blazor Serverで簡易ファイル共有(後編:ファイルダウンロード)

初めまして、アキちゃんです。

備忘録をかねてブログを書くことにしました。

今回はBlazor Serverで簡易ファイル共有(後編:ファイルダウンロード)について書きます。

それでは早速・・・Blazor Serverで簡易ファイル共有(後編:ファイルダウンロード)!

Lesson.1 前回まで

前回まででファイルをアップロードするところまで作成しました。

akr9915.hatenablog.com

今回はアップロードしたファイルをダウンロードするところまでしようと思います

Lesson.2 事前準備

Blazorでファイルをダウンロードする方法は少ないですがいくつかあります。

今回はMicrosoftが紹介している以下サイトの方法で行おうと思います。

learn.microsoft.com

Pages/_Layout.cshtmlに、以下の記述を追記します。

_Layout.cshtml(クリックで展開) _Layout.cshtml(クリックで圧縮)
@using Microsoft.AspNetCore.Components.Web
@namespace FileShare.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<script>
    window.downloadFileFromStream = async (fileName, contentStreamReference) => {
        const arrayBuffer = await contentStreamReference.arrayBuffer();
        const blob = new Blob([arrayBuffer]);
        const url = URL.createObjectURL(blob);
        const anchorElement = document.createElement('a');
        anchorElement.href = url;
        anchorElement.download = fileName ?? '';
        anchorElement.click();
        anchorElement.remove();
        URL.revokeObjectURL(url);
    }
</script>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <base href="~/" />
    <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
    <link href="css/site.css" rel="stylesheet" />
    <link href="FileShare.styles.css" rel="stylesheet" />
    <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
    @RenderBody()
    <div id="blazor-error-ui">
        <environment include="Staging,Production">
            An error has occurred. This application may no longer respond until reloaded.
        </environment>
        <environment include="Development">
            An unhandled exception has occurred. See browser dev tools for details.
        </environment>
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.server.js"></script>
</body>
</html>

Lesson.3 ファイル一覧・ダウンロード

アップロードしたファイル一覧を取得し、任意のファイルをダウンロードするコントローラーを作りたいため、Controllerフォルダに 追加→コントローラー→MVCコントローラー - 空 を選択し、DownloadController.csを作成します。

できたDownloadController.csをざっくり以下のように修正します。

DownloadController.cs(クリックで展開) DownloadController.cs(クリックで圧縮)
using Microsoft.AspNetCore.Mvc;
using System.Text;
using System.Text.Json;
namespace FileShare.Controllers
{
    public class DownloadController : Controller
    {
        private readonly IWebHostEnvironment environment;
        public DownloadController(IWebHostEnvironment environment)
        {
            this.environment = environment;
        }
        [Route("api/getfiles")]
        public IActionResult GetFiles()
        {
            try
            {
                var rslt = new List<string>();
                var files = Directory.GetFiles(Path.Combine(environment.WebRootPath + @"\share"));
                foreach (var i in files)
                {
                    rslt.Add(Path.GetFileName(i));
                }
                return Content(JsonSerializer.Serialize(rslt.ToArray()), "application/json", Encoding.UTF8);
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
        [Route("api/download/file")]
        public async Task<IActionResult> DownloadDocument(IFormFile file)
        {
            try
            {
                using (var memoryStream = new MemoryStream())
                {
                    await file.CopyToAsync(memoryStream);
                    byte[] buffer = memoryStream.ToArray();
                    var fileName = JsonSerializer.Deserialize<string>(Encoding.ASCII.GetString(buffer));
                    var filePath = Path.Combine(environment.WebRootPath + @"\share", fileName);
                    return File(System.IO.File.ReadAllBytes(filePath), "application/octet-stream", fileName);
                };
            }
            catch (Exception ex)
            {
                return StatusCode(500, ex.Message);
            }
        }
    }
}

アップロードしたファイル一覧を表示し、任意のファイルをダウンロードする画面を作りたいため、Pagesフォルダに追加→Razorコンポーネントを選択し、Download.razorを作成します。

できたDownload.razorをざっくり以下のように修正します。

Download.razor(クリックで展開) Download.razor(クリックで圧縮)
@page "/download"
@using System.Text.Json
@inject IJSRuntime JSRuntime
@inject IHttpClientFactory ClientFactory
@foreach (var i in files)
{
    <button @onclick="(s => ButtonClick(i))">@i</button>
}
@code {
    List<string> files = new List<string>();
    protected override async Task OnParametersSetAsync()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:7281" + "/api/getfiles");
        var client = ClientFactory.CreateClient();
        var response = await client.SendAsync(request);
        var getVal = await response.Content.ReadAsStringAsync();
        files.AddRange(JsonSerializer.Deserialize<string[]>(getVal));
    }
    async void ButtonClick(string fileName)
    {
        var content = new MultipartFormDataContent();
        content.Add(new StringContent(JsonSerializer.Serialize<string>(fileName)), "file", "file.json");
        var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:7281" + "/api/download/file");
        var client = ClientFactory.CreateClient();
        request.Content = content;
        var response = await client.SendAsync(request);
        var getVal = await response.Content.ReadAsStreamAsync();
        using var streamRef = new DotNetStreamReference(getVal);
        await JSRuntime.InvokeVoidAsync("downloadFileFromStream", fileName, streamRef);
    }
}

Download.razorにアクセスできるようにするために、Shared/NavMenu.razorを以下のように修正します。

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <nav class="flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="upload">
                <span class="oi oi-data-transfer-upload" aria-hidden="true"></span> Upload
            </NavLink>
        </div>
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="download">
                <span class="oi oi-data-transfer-download" aria-hidden="true"></span> Download
            </NavLink>
        </div>
    </nav>
</div>

ここまできたら実行しUploadで任意のファイルをアップロードします。(今回は困った18.pngをアップロード)

ファイルアップロード後、Downloadに遷移するとUploadで選択したファイル名のボタンが存在しています。(困った24.pngは前回記事で上げたものです。)

任意のボタンを押下しダウンロードが完了したらアップロードしたファイルがダウンロードされているはずです。

Lesson.4 あとがき

以上!!

github.com