jsでファイルを一括ダウンロードしてみる

# はじめに

ファイル１つ２つをダウンロードするときは、特に問題ないのですが、
数が増えてくると、なかなか面倒になってきます。
通常、こういうときには、サーバー側でファイルをzipなどで１つにまとめて、
ダウンロードすればいいのですが、
今回、日本語のファイル名が文字化けしてしまったため、jsで対応してみました。
(linuxでzipしたものが、windowsで文字化け/そろそろwindowsも問題なくなっているかも？)

当初、簡単に考えていたのですが、

- jsでリンクを開いても最後のファイルしかダンロードされない。
- 別タブで開いても動作しない。(そもそもかっこよくない)

などが発生し、ちょっと試行錯誤しましま。


## html
ダウンロードのボタンを配置
ダウンロードの`<a>`タグに

- ダウンロードのリンクにclassを付与
- data-download-fileにファイル名を設定

```html
<button id="download_button">一括ダウンロード</button><br>
<a class="download_file" data-download-file="テスト1.xlsx" href="/1/download">ダウンロード</a><br>
<a class="download_file" data-download-file="テスト2.xlsx" href="/2/download">ダウンロード</a><br>
<a class="download_file" data-download-file="テスト3.xlsx" href="/3/download">ダウンロード</a><br>
・
・
・
```

## javascript
jsでは一旦サーバーからブラウザにデータを保持
その後、そのデータをPCに送るようにしています。

実際には、経過を表示する処理などを入れた方がいいです。


```javascript
    $('#download_button').on('click', function(e){

        // 初期化処理
        var file_count = $(".download_file").length;  // ダウンロード数
        var try_count = 0;
        var err_file = [];

        $('.download_file').each(function() {

            var href = $(this).attr('href');
            var filename = $(this).data('download-file');

            loadFile(href, function (responce) {
                // 経過処理
                try_count++;
                if (responce!=null){
                    downloadAsFile(filename,responce);
                }else{
                    err_file.push(filename);
                }
                if (file_count==try_count){
                    // 終了処理
                    if (err_file.length!=0){
                        // エラー処理
                    }
                }
            });
        });
    });

    // サーバーからブラウザにロード
    var loadFile = function(href, cb) {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(){
            if (this.readyState == 4 ){
                if ( this.status == 200) {
                    cb(this.response);
                }else{
                    cb(null);
                }
            }
        }
        xhr.open('GET', href, true);
        xhr.responseType = 'blob';
        xhr.send(null);
    }

    // ブラウザからPCにダウンロード
    var downloadAsFile = function(filename, blob) {
        var objectURL = window.URL.createObjectURL(blob);
        var link = document.createElement("a");
        document.body.appendChild(link);
        link.href = objectURL;
        link.download = filename;
        link.click();
        document.body.removeChild(link);
    };
```

ファイル名をサーバーから送られたheaderから取得することは可能です。(やってないけど)

```js
if ( this.status == 200) {
    var header = this.getResponseHeader('Content-Disposition');
    console.log(header);
```
このあと、headerをattachmentで切り取って、ゴニョゴニョすれば ( [参考まで](https://qiita.com/yukinagae/items/9e64d8ad93c65584bee9) )


### 注意事項
- excelファイルのダウンロードを想定しています。
- chrome以外で動作確認をしていません。
- ファイルサイズが大きいときの動作確認をしていません。
- ファイル数が多いときの動作確認をしていません。(50個ぐらいまでは確認しました)
- サーバー側はエラーのときリダイレクトはしないでください。(リダイレクト先がダウンロードされます。)

### 参考リンク
- [バイナリファイルをAjaxで取得する際に注意する点](https://qiita.com/tom_konda/items/484955b8332e0305ebc4)
- [AjaxでバイナリのJPEG画像データを受け取って表示する](https://qiita.com/Yarimizu14/items/f56123c738f12ad1844a)
- [JavaScriptからファイルをダウンロードさせるのはどうしたら良いですか？](https://ja.stackoverflow.com/questions/300/javascript%E3%81%8B%E3%82%89%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E3%81%95%E3%81%9B%E3%82%8B%E3%81%AE%E3%81%AF%E3%81%A9%E3%81%86%E3%81%97%E3%81%9F%E3%82%89%E8%89%AF%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B)
- [ファイルをダウンロードさせるには？［JavaScript］](http://www.atmarkit.co.jp/ait/articles/1603/30/news026.html)