Ajaxで複数ファイルをダウンロードさせる方法

さてさて、ここのところLaravelTwilioの話題ばかりで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にアクセスすることになります。

通常のaxiosAjaxだとシンプルに以下のようにしますが、今回はresponseTypeblobを指定していることに注目してください。(つまり、バイナリデータとして処理しています)

axios.get(url)
    .then(response => {

        // ここで何かを実行
        
    });

そして、then()内ではダウンロードURLから取得したレスポンス・データからblobインスタンスをつくり、<a>タグをつくってhrefに作成したblobデータをセットしています。

さらに、この<a>タグにはHTML 5download属性をつけて、クリックされるとページ移動ではなく、ダウンロードが実行されるようになります。(つまり、このタグは以下のようになります)

<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 11axiosが使えるようにするために以下の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 11OKバージョンの両方が入っています)

※ ただし、ダウンロードするURLはご自身で用意してください。

Ajaxで複数ファイルをダウンロードさせる

おわりに

ということで今回はAjaxを使って一気に複数ファイルをダウンロードさせる方法をご紹介しました。

この方法を使えば、いちいち一個ずつボタンをクリックしてダウンロードしなくてもよくなるので、ユーザビリティの向上にもつながるかと思います。

なお、JavaScriptを使って複数のファイルを「Zip化」してダウンロードするzip.jsというライブラリもあったりしますので、こちらも活用してみるのもいいかもしれません。

ぜひ皆さんの開発でも使ってみてくださいね。

ではでは〜😊✨

この記事が役立ちましたらシェアお願いします😊✨