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

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

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

```html:.razor
<InputFile OnChange="OnInputFileChange" />
```

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/af202aa3-7bf8-5094-68de-b6fb3aa421e8.png)

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

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

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

https://docs.microsoft.com/ja-jp/aspnet/core/blazor/file-uploads

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

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

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

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

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

https://qiita.com/jsakamoto/items/ec2f441cbf591fb04640

そんな場合の自分のオススメは、**"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 です。

```shell
dotnet add package Toolbelt.Blazor.FileDropZone
``` 

## 2. `<InputFile>` を `<FileDropZone>` で囲む

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

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

```html:.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 クラス名などを付けておいて、

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

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

```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 アニメのとおり、ファイルのホバー時の見た目の変化を与えることができます。

![movie](https://raw.githubusercontent.com/jsakamoto/Toolbelt.Blazor.FileDropZone/master/.assets/movie.001.gif)

いいですね! 👍

# おわりに

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

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

https://jsakamoto.github.io/Toolbelt.Blazor.FileDropZone/

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