Vue.jsでボタンを押したらダウンロードってのをいい感じにやりたかった
とあるボタンをクリックしたら、別のURLからファイルをしてくるようにしたい。
やりたかったのは、リンククリック→データを集計してCSVを作成→ダウンロードと言う流れ。
ダウンロードに失敗したときなど、もとのページでエラー通知などを出したいので、直接DL用のURLに飛ばすことはしたくない。
こんな事情があったのですが、なかなか詰まってしまいました。。
Axiosを使って非同期でファイル取得
とあるボタンを押したら、Axiosでリクエストを飛ばしてファイルをダウンロードさせたい、というようなことがあるかと思います。
そのような際は、以下のように、
・responseTypeをblobに指定
・html elementを生成して遷移させる
というようにすることで、ダウンロードが可能です。
(ほぼほぼ参考サイトのコピーですが)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
axios({ url: 'http://api.dev/file-download', method: 'GET', // POSTでもok responseType: 'blob', // これがないと文字化けする }).then((response) => { const url = URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; link.setAttribute('download', 'file.csv'); //ここらへんは適当に設定する document.body.appendChild(link); link.click(); link.revokeObjectURL(); }); |
参考)https://thewebtier.com/snippets/download-files-with-axios/
各ブラウザでダウンロード出来るようにする
上記のコードだと、IE等ではダウンロードすることが出来ません。
そのため、現実に運用する際はIE・Edge用に処理を加えた以下の様なコードが基本となります。
(ついでに関数化したものを置いておきます。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public static downloadCsvBlob(blob, fileName) { if (window.navigator.msSaveOrOpenBlob) { // for IE,Edge window.navigator.msSaveOrOpenBlob(blob, fileName); } else { // for chrome, firefox const url = URL.createObjectURL(new Blob([blob], {type: 'text/csv'})); const linkEl = document.createElement('a'); linkEl.href = url; linkEl.setAttribute('download', fileName); document.body.appendChild(linkEl); linkEl.click(); URL.revokeObjectURL(url); linkEl.parentNode.removeChild(linkEl); } } axios({ url: 'http://api.dev/file-download', method: 'GET', // POSTでもok responseType: 'blob', // これがないと文字化けする }).then((response) => { downloadCsvBlob(response.data, 'file.csv') }); |
vue.jsだったりでフロントエンドとサーバーサイドを分離しようとしている場合にはよくある場面ではないでしょうか。
ここらへんは意外とWeb上に情報が少ない、特に日本語は…