Blazor Serverでフォルダ内のファイルをストリーミングでサーバーへアップロードする方法

BlazorServer(.NET 8/Interactive Server)でクライアントサイドで選択したフォルダ内に含まれる複数のファイルをストリーミングを使ってサーバーへアップロードして一時フォルダに保存する最小構成のサンプル。

今回はクライアントサイドスクリプトをMicrosoft.TypeScript.MSBuildパッケージを使ってTypeScriptで書いてみましたが、トランスパイル済のJavaScriptソースも掲載しています。


画面構成

Inputで選択したフォルダ内のファイルをUploadボタンでアップロードします。

file


サンプルソースコード(C#/TypeScript/JavaScript)

エラー処理は適宜実装してください。保存先はC:\temp\決め打ちです。

Uploader.razor:

@page "/uploader"
@rendermode InteractiveServer
@using System.Runtime.CompilerServices
@implements IAsyncDisposable
@inject IJSRuntime JS

<input id="fileInput" type="file" webkitdirectory multiple disabled="@disabled" />
<button @onclick="OnUploadClick" disabled="@disabled">Upload</button>

@code {
    private IJSObjectReference? module;

    private bool disabled;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);

        if (firstRender)
        {
            module = await JS.InvokeAsync<IJSObjectReference>(
                "import",
                "./Components/Pages/Uploader.razor.js");
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (module != null)
        {
            await module.DisposeAsync();
        }
    }

    private async Task OnUploadClick()
    {
        if (module != null)
        {
            var jsor = await module.InvokeAsync<IJSObjectReference>("getFileInfo", "fileInput");
            if (jsor != null)
            {
                disabled = true;
                StateHasChanged();

                var numOfFiles = await jsor.InvokeAsync<int>("getCount");
                for (var i = 0; i < numOfFiles; ++i)
                {
                    var name = await jsor.InvokeAsync<string>("getName", i);
                    // パスは今回のサンプルでは使わないのでコメントアウト
                    // var relativePath = await jsor.InvokeAsync<string>("getRelativePath", i);
                    var size = await jsor.InvokeAsync<int>("getSize", i);
                    var streamRef = await jsor.InvokeAsync<IJSStreamReference>("getStream", i);
                    using var readStream = await streamRef.OpenReadStreamAsync(maxAllowedSize: 1024 * 1024);
                    // サーバー側にファイルを保存
                    var tempFilePath = Path.Combine(@"C:\temp", $"{i:D3}_{name}");
                    using FileStream fs = new FileStream(tempFilePath, FileMode.Create);
                    await readStream.CopyToAsync(fs);
                }

                disabled = false;
                StateHasChanged();
            }
        }
    }
}

Uploader.razor.ts

export async function getFileInfo(inputId: string): Promise<FileInfo | null> {
    const input = <HTMLInputElement>document.getElementById(inputId);
    if (!input || !input.files || input.files.length == 0)
        return null;

    return new FileInfo(input.files);
}

class FileInfo {
    private files: File[] = [];

    constructor(files: FileList) {
        for (let i = 0; i < files.length; ++i) {
            const file = files.item(i);
            if (file)
                this.files.push(file);
        }
    }

    getCount(): number {
        return this.files.length;
    }

    getName(index: number): string {
        return this.files[index].name;
    }

    getRelativePath(index: number): string {
        return this.files[index].webkitRelativePath;
    }

    getSize(index: number): number {
        return this.files[index].size;
    }

    getStream(index: number): File {
        return this.files[index];
    }
}

Uploader.razor.js(トランスパイル後/ES2022):

export async function getFileInfo(inputId) {
    const input = document.getElementById(inputId);
    if (!input || !input.files || input.files.length == 0)
        return null;
    return new FileInfo(input.files);
}

class FileInfo {
    files = [];
    constructor(files) {
        for (let i = 0; i < files.length; ++i) {
            const file = files.item(i);
            if (file)
                this.files.push(file);
        }
    }
    getCount() {
        return this.files.length;
    }
    getName(index) {
        return this.files[index].name;
    }
    getRelativePath(index) {
        return this.files[index].webkitRelativePath;
    }
    getSize(index) {
        return this.files[index].size;
    }
    getStream(index) {
        return this.files[index];
    }
}


以上です。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする