九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、ここのところLaravel
やTwilio
の話題ばかりでJavaScript
がメインになる記事をお届けできていませんでした。
そして、ちょうどというか偶然というかひとつJavaScript
である機能をつくることがあったので、今回はそのテクニックをご紹介したいと思います。
その内容とは・・・
Ajaxで複数ファイルをダウンロードさせる方法
です。
例えば、JavaScript
で1つファイルをダウンロードさせるには、以下のように強制的にリダイレクトしてやればOKです。
location.href = '(ダウンロードするURL)';
しかも、移動先がダウンロード用のヘッダーを送信していれば実際にはページ移動することなくダウンロードだけできるようになります。
ただし、この場合1つだけ制限があって、「たった1つのファイルだけしかダウンロードできない」んです。
つまり、例えば「検索結果として表示されているデータの中からいくつか選択し、それらに関連する全ファイルをダウンロードする」といった場合にはlocation.href
は使えないということになります。
そこで!
今回はこの「複数ダウンロード」を実現するテクニックをAjax
を使って実装してみたいと思います。
ぜひ皆さんのお役に立てると嬉しいです😊✨
実行環境: axios 0.19(ダウンロードテスト環境としてLaravel 6.x)
目次
テキストファイルをダウンロードできるようにする
まずはテストとして、Laravel
で「アクセスするとテキストファイルがダウンロードできる」URLをつくります。
※ 不要の場合は次がメインのコードになりますのでそちらまで読み飛ばしてください。
routes/web.php
を開いて以下のコードを追加してください。
Route::get('test_download/{number}', function($number){ return response()->streamDownload(function() use($number) { echo $number .'番目のテキストファイルです。'; }, 'test.txt'); });
これで、/test/download/(数字)
にアクセスするとテキストファイルがダウンロードされることになります。
※ 今回はテストですので、ルートの中で全て完結するコードにしますが、本番環境ではきちんとコントローラーをつくることをおすすめします。
Ajaxで複数ファイルをダウンロードする(ES6バージョン)
では、ここからがメインのコードになりますが、ここではES6
(アロー関数などの新しいJavaScript
)の書き方になっています。
そのため、IE 11
もサポートしたい場合は次の「Ajaxで複数ファイルをダウンロードする(IE 11もOKバージョン)」をご覧ください。
<html> <body> <div> <button type="button" onclick="onClick()">ダウンロードする</button> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script> <script> function onClick() { for(let i = 1 ; i <= 3 ; i++) { // 3ファイルをダウンロード let filename = 'test_text_'+ i +'.txt'; axios({ url: '/test_download/'+ i, method: 'get', responseType: 'blob', }) .then(response => { const blob = new Blob([response.data]); const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.setAttribute('download', filename); document.body.appendChild(link); link.click(); }) .catch(error => { // エラー処理 }); } } </script> </body> </html>
この中では、まず以下のようなボタンをつくります。
そして、このボタンがクリックされたときに実行されるのがonClick()
で、この中ではfor()
で3回ループするようにし、その度にaxios
を使ってダウンロードURLにアクセスすることになります。
通常のaxios
のAjax
だとシンプルに以下のようにしますが、今回はresponseType
にblob
を指定していることに注目してください。(つまり、バイナリデータとして処理しています)
axios.get(url) .then(response => { // ここで何かを実行 });
そして、then()
内ではダウンロードURLから取得したレスポンス・データからblob
インスタンスをつくり、<a>
タグをつくってhref
に作成したblob
データをセットしています。
さらに、この<a>
タグにはHTML 5
のdownload
属性をつけて、クリックされるとページ移動ではなく、ダウンロードが実行されるようになります。(つまり、このタグは以下のようになります)
<a href="blob:http://******" download="test_text_1.txt"></a>
そして最終的にこの<a>
タグを<body>
タグに追加し、click()
メソッドを実行することで自動的に「クリックしたもの」として処理をさせています。
Ajaxで複数ファイルをダウンロードする(IE 11もOKバージョン)
先ほどのコードはES6
を使った比較的新しいJavaScript
のコードの書き方でしたが、残念ながら(よくあることですが😫)IE 11
では動きません。
そのため、この項目ではIE 11
にも対応したコードをご紹介します。
<html> <body> <div> <button type="button" onclick="onClick()">ダウンロードする</button> </div> <script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script> <script> function onClick() { for(let i = 1 ; i <= 3 ; i++) { // 3ファイルをダウンロード let filename = 'test_text_'+ i +'.txt'; axios({ url: '/test_download/'+ i, method: 'get', responseType: 'blob', }) .then(function(response) { const blob = new Blob([response.data]); if(typeof window.navigator.msSaveBlob !== 'undefined') { // IE 11 window.navigator.msSaveBlob(blob, filename); } else { const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.setAttribute('download', filename); document.body.appendChild(link); link.click(); } }) .catch(function() { // エラー処理 }); } } </script> </body> </html>
ES6
バージョンと違う部分は、3つです。
まず、基本的なことですがIE 11
ではアロー関数は使えませんので全て旧式のfunction(){ *** }
という書き方に変更しています。
そして2つ目は、axios
(Ajax用のパッケージ)ですが、これもIE 11
ではそのままでは動きません。(Promise
が原因のようです)
そのため、IE 11
でaxios
が使えるようにするために以下のcdn
を読み込んでいます。
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.min.js"></script>
そして最後に、<a>
タグのdownload
要素もIE 11
では使えないため、代わりにmsSaveBlob
を使っています。(というか、これがあるならdownload
のエイリアス的にしてくれればいいのに・・・という気はしなくもありません😂)
window.navigator.msSaveBlob(blob, filename);
ダウンロードする
実際に今回開発したソースコードを以下からダウンロードすることができます。(ES6
バージョン、IE 11
もOK
バージョンの両方が入っています)
※ ただし、ダウンロードするURLはご自身で用意してください。
Ajaxで複数ファイルをダウンロードさせるおわりに
ということで今回はAjax
を使って一気に複数ファイルをダウンロードさせる方法をご紹介しました。
この方法を使えば、いちいち一個ずつボタンをクリックしてダウンロードしなくてもよくなるので、ユーザビリティの向上にもつながるかと思います。
なお、JavaScript
を使って複数のファイルを「Zip化」してダウンロードするzip.jsというライブラリもあったりしますので、こちらも活用してみるのもいいかもしれません。
ぜひ皆さんの開発でも使ってみてくださいね。
ではでは〜😊✨