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 に、以下を記述します。
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)
コメントの投稿