Blazor WebAssembly でのファイル ドラッグ&ドロップの実装 JavaScript との相互運用編

2024/10/20
★★

Blazor WebAssembly でのファイル ドラッグ&ドロップの実装の検討

以前の記事で、Blazor WebAssembly でのファイル ドラッグ&ドロップの実装を JavaScript との相互運用 を使用しないで行う方法を説明しました。

今回は、JavaScript との相互運用 の使用を最小限に、ファイル ドラッグ&ドロップの実装を行う方法を説明します。

JavaScript の利用を最小限に

前回の記事で詳細を記載していますが、Blazor WebAssembly でファイル ドラッグ&ドロップの実装を完結できない理由としては、 drop イベント の引数 DragEvent のプロパティ DataTransfer.files でドロップされたファイルの情報が取得できないことです。

であれば、drop イベントを JavaScript で定義、以降のファイル操作も JavaScript で実装すれば良いということになりますが、JavaScript での実装を最小限にするために、 drop イベントをフックし、InputFile の Change イベントへディスパッチする方法を取りたいと思います。 この方法であれば、 C# で記述する Change イベントにファイル操作の処理を共通化することができます。

Drag イベントをChange イベントへディスパッチする JavaScript モジュールの作成

[プロジェクト] - [wwwroot] 以下に js フォルダーを作成し、同フォルダー以下に、changeEventDispacher.js を作成します。

changeEventDispacher.js

changeEventDispacher.js に、以下を記述します。

export const ChangeEventDispacher = (dropContainerElement, inputFile) => {

    const onDrop = (e) => {
        e.preventDefault();
		// Drop イベントで取得したファイル一覧を inputFile に設定
        inputFile.files = e.dataTransfer.files;
        const event = new Event("change");
		// Change イベントを発火
        inputFile.dispatchEvent(event);
    };
    
	// Drop イベントのレジスト
    dropContainerElement.addEventListener("drop", onDrop);

    return {
        dispose: () => {
			// Drop イベントのアンレジスト
            dropContainerElement.removeEventListener("drop", onDrop);            
        }
    }
}

ChangeEventDispacher がインスタンス化されると、コンストラクタの引数で指定された dropContainerElement に dorp イベントをレジストします。
drop イベント発火時には、引数で取得したファイル一覧をコンストラクタの引数 inputFile の files プロパティに設定し Change イベントを発火します。

ドロップ領域とモジュールのロードの記述

Razor ページに dorp イベントのディスパッチ先の InputFile とファイルドロップ領域を記述します。
この時、@ref で、InputFile 及びドロップ領域の参照を private フィールドに保持します。

@inject IJSRuntime _jsRuntime
@implements IAsyncDisposable

<h3 class="mb-4">ドロップ フィールド JS 相互運用 送信先 InputFile</h3>
<InputFile @ref="_inputFile" class="mb-4" OnChange="OnChange" />

<h3 class="mb-4">ドロップ フィールド JS 相互運用</h3>
<div @ref="_dropContainerElement" class="row bg-secondary align-items-center mb-5" style="min-height: 100px;"
	@ondragenter:stopPropagation @ondragenter:preventDefault
	@ondragover:stopPropagation @ondragover:preventDefault>
	<div class="col text-center">
		ここにファイルをドロップしてください。
	</div>		
</div>

@code {
	private ElementReference _dropContainerElement;
	private InputFile? _inputFile;

    private void OnChange(InputFileChangeEventArgs e)
    {
        // 処理
    }
}

ドロップ フィールド

次に、先に作成した changeEventDispacher.js からモジュールをロードして ChangeEventDispacher をインスタンス化します。インスタンス化時に、引数として、@ref で保持した InputFile、ドロップ領域の参照を渡します。

private IJSObjectReference? _module;
private IJSObjectReference? _changeEventDispacher;

protected override async Task OnAfterRenderAsync(bool firstRender)
{
	if (firstRender)
	{
		// モジュールのロード
		_module = await _jsRuntime.InvokeAsync<IJSObjectReference>(
			"import", "./js/changeEventDispacher.js");

		if (_inputFile?.Element is not null)
		{
			// モジュールから ChangeEventDispacher クラスをインスタンス化
			_changeEventDispacher = await _module.InvokeAsync<IJSObjectReference>(
				"ChangeEventDispacher", _dropContainerElement, _inputFile.Element);
		}
	}
}

public async ValueTask DisposeAsync()
{
	if (_changeEventDispacher is not null)
	{
		await _changeEventDispacher.InvokeVoidAsync("dispose");
		await _changeEventDispacher.DisposeAsync();
	}

	if (_module is not null)
	{
		await _module.DisposeAsync();
	}
}

Razor ページで @inject IJSRuntime _jsRuntime とすることで、DI により IJSRuntime のインスタンスを取得できます。IJSRuntime.InvokeAsync で Blazor WebAssembly から JavaScript を実行できます。
また、private フィールドに、ロードしたモジュール、モジュール内のクラス インスタンスを保持し、DisposeAsync でリソースを破棄できるようにします。 DisposeAsync は、@implements IAsyncDisposable の実装で、リソース破棄時に呼び出されます。

以上で、ドロップ領域にファイルがドロップされた際に、InputFile.OnChange イベントが発火するようになります。 InputFile.OnChange イベント引数 InputFileChangeEventArgs からファイル一覧を取得しファイル ストリームを開くこともできます。

private void OnChange(InputFileChangeEventArgs e)
{
	var files =  e.GetMultipleFiles();

	foreach (var file in files)
	{
		_logger.LogInformation(
			"ファイル名: {filename}, サイズ: {size}, 更新日時: {lastModified}",
			file.Name, // ファイル名
			file.Size, // ファイル サイズ
			file.LastModified // ファイル更新日時
		);

		// ファイル ストリームのオープン
		using var stream = file.OpenReadStream();

	}
}

以降のファイル アップロードの処理については、以下の記事を参考にしてください。

コメント (0)

コメントの投稿