Blazor でファイルのドラッグ & ドロップを受け付ける


Blaozr WebAssembly でも Server でも、<input type="file"/> によるファイル選択とアップロードは普通に実装できる

Blazor による Web アプリケーションの構築時、ファイルのアップロード機能が必要となった際は、下記のように、<InputFile> Razor コンポーネントを配置して実装することでしょう。

.razor
<InputFile OnChange="OnInputFileChange" />

つまるところ、<InputFile> Razor コンポーネントは <input type="file"/> HTML 要素としてレンダリングされ、ファイル選択のユーザーインターフェースが提供されます (下図)。

そして、この INPUT 要素のユーザー操作によってファイルが選択されると、<InputFile> Razor コンポーネントの OnChange イベントコールバックが呼び出され、イベントコールバックのハンドラ内ではその選択されたファイルの Stream を取得できるので、そこで色々よしなにできるという仕組みです。

この辺りの造りは、Blazor WebAssembly でも Blazor Server でも、ほぼ同じです。

その先の処理についてのガイドも含め、Microsoft 公式の Docs サイトにも、「ASP.NET Core Blazor ファイルのアップロード」という、そのものズバリのタイトルのドキュメントが掲載されています (下記リンク)。

じゃぁ、ファイルのドラッグ & ドロップは?

ではでは、<input type="file"/> HTML 要素のユーザー操作ではなくて、ファイルのドラッグ & ドロップでファイル選択を受け付けるようにするにはどう実装すればよいでしょうか?

実のところ、<input type="file"/> HTML 要素上にファイルをドラッグ & ドロップすれば、それでドロップしたファイルが選択されます。
それで充分であれば、追加で何かを実装する必要はありません。

しかし実際的な場面では、<input type="file"/> HTML 要素に限定せず、もっと広い表示領域で、ファイルのドラッグ & ドロップを受け付けたくなることでしょう。

および、下記記事のような手法で「カッコの良い・見栄えのする」ファイル選択 UI を作った場合、この技法では <input type="file"/> 要素は非表示にしてしまうため、<input type="file"/> HTML 要素上にファイルをドラッグ & ドロップする手法はそもそも使えません。

そんな場合の自分のオススメは、"Blazor File Drop Zone" NuGet パッケージの利用です。

<FileDropZone> タグで囲むだけで OK!

"Blazor File Drop Zone" NuGet パッケージを利用して、ファイルのドラッグ & ドロップを受け付けるようにするには、以下のように実装すればよいです。

1. プロジェクトに "Blazor File Drop Zone" の参照を追加

まず何はともあれ、プロジェクトに "Blazor File Drop Zone" NuGet パッケージへのパッケージ参照を追加します。
Visual Studio をお使いなら、「パッケージの管理」NuGet パッケージマネージャ GUI から行なっても良し、dotnet CLI をお使いなら以下のコマンドを実行することで OK です。

dotnet add package Toolbelt.Blazor.FileDropZone

2. <InputFile><FileDropZone> で囲む

ひとたびパッケージ参照を追加すれば、名前空間 Toolbelt.Blazor.FileDropZone 内にある <FileDropZone> Razor コンポーネントが使えるようになります。

この <FileDropZon> Razor コンポーネントで、<InputFile OnChange="OnInputFileChange" /> (を含むお好みの領域) を囲います (下記コード例)。

.razor
@using Toolbelt.Blazor.FileDropZone
...
<FileDropZone>
  ...
  <InputFile OnChange="OnInputFileChange" />
  ...
</FileDropZone>

<FileDropZone> は最終的には <div> 要素としてレンダリングされます。
および、<FileDropZone> で囲った子コンテンツはその <div 要素内の子孫としてレンダリングされます。
そして、なんと、この <FileDropZone> がレンダリングした <div> 要素が、ファイルのドラッグ & ドロップを受け付けるようになっているのです!

で、ドロップされたファイルはどこへ行くのか? というと、<FileDropZone> で囲った中の <InputFile> コンポーネントに、すなわち、<InputFile>OnChange イベントコールバックの発火につながるのです。👍

このような仕組みで、Blazor に用意されているファイルアップロードの仕組みはそのままに、その上で、<FileDropZone> コンポーネントで囲うだけで、ファイルのドラッグ & ドロップも受け付けるように拡張できる次第です。

3. 動作のフィードバックのために CSS を追加

なお、ここまでの実装だけだと、実際にファイルをドラッグして <FileDropZone> の領域に持っていったとしても、見た目上なんの変化・フィードバックもないため、ファイルをドロップ可能であることがさっぱりわかりません。

幸い、<FileDropZone> Razor コンポーネントはその搭載機能の1つとして、ファイルが要素の上空にドラッグされてくると、hover という CSS クラスが追加されるようになっています。

つまり、まずは修飾しやすいように、<FileDropZone> に何かわかりやすい CSS クラス名などを付けておいて、

.razor
...
                <!-- 👇 区別しやすいよう drop-zone の CSS クラスを付けた -->
<FileDropZone class="drop-zone">
  ...

以下のように、hover CSS クラスの有無で変化を付けた見た目を CSS スタイルシートで定義してやれば (※なお下記は Blazor の分離 CSS 機能上での実装例)、

.razor.css
::deep .drop-zone {
    padding: 32px;
    border: dashed 2px transparent;
    transition: border linear 0.2s;
}

/* 👇 hover CSS クラスが付いたら、オレンジ色の破線の枠を表示するよう定義 */
::deep .drop-zone.hover {
    border: dashed 2px darkorange;
}

下図 GIF アニメのとおり、ファイルのホバー時の見た目の変化を与えることができます。

いいですね! 👍

おわりに

この "Blazor File Drop Zone"、もちろん、Blazor WebAssembly でも Blazor Server でも、どちらのホスティングモデルでも動作します。

また、下記 URL で、<FileDropZone> を使った作例のライブデモを公開しています。
実際の動きを試すことができるので、参考にどうぞ。

以上、Blazor でファイルのドラッグ & ドロップによるアップロードを実装するなら、普通に <InputFile> で組んだ上で、<FileDropZone> で囲めば OK! という話でした。