blazorにはwebappのFileResultを使用したPostBackような動的にファイルコンテンツを作成して、ファイルをダウンロードさせる仕組みが無い。
では、どうやってファイルコンテンツを作成して、ダウンロードさせるかを調べてみた。
普通に考えると、1番簡単なのは、コールバックでコンテンツを作成して、Data URLに変換し、そのリンクを表示させる方法だが、これだと、作成のトリガーとリンクをクリックするという、2アクションが必要になる。
結局、作成トリガー(例えばボタンクリック)でファイルのダウンロードまで、行うには、Javascriptの助けを借りることになる。
以下のような感じで可能だが、サイズ制限(MSのサイトでは250Mbytes以下と記述されていた)があるようだ。
//
// ファイルをダウンロードするJSヘルパー
// やっていることは単純で、StreamからBlobを作成してそのurlをもった、
// aタグを生成し、aタグに対してclickイベントを発生させた後、aタグとdata url
// に割いているリソースを開放。
// 結構良く使う手段だね。blazor独自の点は.NETのStreamを
// Javascript用にWrappingしてarrayBufferを取得している点ぐらい。
var mylibrary = {
DownloadFile : async (StreamRef, FileName) => {
const arrayBuffer = await StreamRef.arrayBuffer();
const blob = new Blob([arrayBuffer]);
const url = URL.createObjectURL(blob);
const anchorElement = document.createElement('a');
anchorElement.href = url;
anchorElement.download = FileName ?? '';
anchorElement.click();
anchorElement.remove();
URL.revokeObjectURL(url);
}
};
using(MemoryStream mstm = new MemoryStream()) {
// コンテンツ作成
await mstm.FlushAsync();
// 念のため
mstm.Seek(0L,SeekOrigin.Begin);
// Javascriptに渡すためのStreamリファレンス作成
using var stmRef = new DotNetStreamReference(stream: mstm);
// Javascript呼出
await JSRuntime.InvokeVoidAsync("mylibrary.DownloadFile",stmRef,FileName);
}
大きいファイルの場合は、物理的にファイルを作成して、そのリンクでaタグ作成および、クリック・・・という形を取れとのこと。
何か他に良い方法は無いものだろうか・・・
ファイルダウンロードの標準的な仕組みをblazor内で定義して欲しいものだ。