使用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表单,因为表单数据相当大,并且是动态创建的。

上面的代码有问题吗?我错过了什么?我只是不知道如何将数据作为下载发送到客户端。

如果您在发送请求之前将XMLHttpRequest.responseType属性设置为'blob',那么当您收到响应时,它将被表示为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 (e) {
    var blob = e.currentTarget.response;
    var contentDispo = e.currentTarget.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 () { });
}

如果你不关心是否让浏览器导航到文件,当它是一个可见的文件类型,那么创建一个总是直接保存到file的方法要简单得多:

function saveBlob(blob, fileName) {
    var a = document.createElement('a');
    a.href = window.URL.createObjectURL(blob);
    a.download = fileName;
    a.dispatchEvent(new MouseEvent('click'));
}

UPDATE:自Blob API引入以来,这个答案不再准确。详情请参考Steven的回答。


原始答:

XHR请求不会触发文件下载。我找不到明确的要求,但是XMLHttpRequest上的W3C文档也没有描述对content-disposition=attachment响应的任何特殊反应

如果不是POST请求,可以在单独的选项卡中通过window.open()下载文件。这里建议使用target=_blank

的隐藏表单。

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);
	},

对于我来说,它与fetch API一起工作。我无法使它在React/Typescript项目中使用XMLHttpRequest。也许是我不够努力。不管怎样,问题解决了。下面是代码:

const handleDownload = () => {
    const url = `${config.API_ROOT}/download_route/${entityId}`;
    const headers = new Headers();
    headers.append('Authorization', `Token ${token}`);
    fetch(url, { headers })
        .then((response) => response.blob())
        .then((blob) => {
            // Create  blob  URL
            const blobUrl = window.URL.createObjectURL(blob);
            // Create a temporary anchor el
            const anchorEl = document.createElement('a');
            anchorEl.href = blobUrl;
            anchorEl.download = `case_${entityId}_history.pdf`; // Set any filename and extension
            anchorEl.style.display = 'none';
            // Append the a tag to the DOM and click it to trigger download
            document.body.appendChild(anchorEl);
            anchorEl.click();
            // Clean up
            document.body.removeChild(anchorEl);
            window.URL.revokeObjectURL(blobUrl);
        })
        .catch((error) => {
            console.error('Error downloading file:', error);
        });
};

最新更新