web-dev-qa-db-ja.com

XMLHttpRequestを使用したファイルの迅速なダウンロード

JQueryのajaxメソッドはダウンロードを処理できないことを承知しており、これを行うためのjQueryプラグインを追加したくありません。

POSTデータをXMLHttpRequestで送信してファイルをダウンロードする方法を知りたい。

これが私が試したものです:

var postData = new FormData();
postData.append('cells', JSON.stringify(output));

var xhr = new XMLHttpRequest();
xhr.open('POST', '/export/', true);
xhr.setRequestHeader("X-CSRFToken", csrftoken);
xhr.responseType = 'arraybuffer';
xhr.onload = function (e) {
    console.log(e);
    console.log(xhr);
}
xhr.send(postData);

私はDjangoを使用していますが、ファイルはクライアントに正常に返送されているようです。 Chromeのネットワークタブで、プレビュータブに意味不明な文字が表示されます(これは予想通りです)。しかし、Zipファイルのテキスト表現ではなく、Zipファイルを送り返したいのですが。これがDjangoバックエンドです:

wrapper = FileWrapper(tmp_file)
response = HttpResponse(wrapper, content_type='application/Zip')
response['Content-Disposition'] = "attachment; filename=export.Zip"
response['Content-Length'] = tmp_file.tell()
return response

XMLHttpRequestsでこれを行う方法の適切な例を見つけることなく、これを何時間も検索しました。 POSTアクションで適切なhtmlフォームを作成したくありません。フォームデータはかなり大きく、動的に作成されるためです。

上記のコードに何か問題がありますか?何か不足していますか?実際にダウンロードとしてクライアントにデータを送信する方法がわかりません。

24
bozdoz

XHRリクエストはファイルのダウンロードをトリガーしません。明示的な要件を見つけることはできませんが、 XMLHttpRequestのW3C文書 はcontent-disposition = attachment応答に対する特別な反応も説明していません

POSTリクエストでない場合は、別のタブでwindow.open()を使用してファイルをダウンロードできます。 ここ target = _blankで隠しフォームを使用することが提案されました

[〜#〜] upd [〜#〜]Blob API の導入により、この回答は正確ではなくなりました。詳細については、Stevenの回答を参照してください。

16
Marat

リクエストを送信する前にXMLHttpRequest.responseTypeプロパティを'blob'に設定した場合、レスポンスが返されると、それはblobとして表されます。次に、ブロブを一時ファイルに保存して、そこに移動できます。

var postData = new FormData();
postData.append('cells', JSON.stringify(output));

var xhr = new XMLHttpRequest();
xhr.open('POST', '/export/', true);
xhr.setRequestHeader('X-CSRFToken', csrftoken);
xhr.responseType = 'blob';
xhr.onload = function (this, event) {
    var blob = this.response;
    var contentDispo = this.getResponseHeader('Content-Disposition');
    // https://stackoverflow.com/a/23054920/
    var fileName = contentDispo.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)[1];
    saveOrOpenBlob(blob, fileName);
}
xhr.send(postData);

そして、ここにsaveOrOpenBlobの実装例があります:

function saveOrOpenBlob(blob, fileName) {
    window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
    window.requestFileSystem(window.TEMPORARY, 1024 * 1024, function (fs) {
        fs.root.getFile(fileName, { create: true }, function (fileEntry) {
            fileEntry.createWriter(function (fileWriter) {
                fileWriter.addEventListener("writeend", function () {
                    window.location = fileEntry.toURL();
                }, false);
                fileWriter.write(blob, "_blank");
            }, function () { });
        }, function () { });
    }, function () { });
}

表示可能なファイルタイプである場合に、ブラウザでファイルに移動する必要がない場合は、常にファイルに直接保存するメソッドを作成する方がはるかに簡単です。

function saveBlob(blob, fileName) {
    var a = document.createElement('a');
    a.href = window.URL.createObjectURL(blob);
    a.download = fileName;
    a.dispatchEvent(new MouseEvent('click'));
}
30
Steven Doggart
download: function(){
    var postData = new FormData();
                var xhr = new XMLHttpRequest();
                xhr.open('GET', downloadUrl, true);
                xhr.responseType = 'blob';
                xhr.onload = function (e) {
                        var blob = xhr.response;
                        this.saveOrOpenBlob(blob);
                }.bind(this)
                xhr.send(postData);
 }

saveOrOpenBlob: function(blob) {
                var assetRecord = this.getAssetRecord();
                var fileName = 'Test.mp4'
                var tempEl = document.createElement("a");
        document.body.appendChild(tempEl);
        tempEl.style = "display: none";
        url = window.URL.createObjectURL(blob);
        tempEl.href = url;
        tempEl.download = fileName;
        tempEl.click();
                window.URL.revokeObjectURL(url);
        },

これを試してみてください。

2
Kamlesh Kumar