我在服务器端有一个Struts2操作用于文件下载。
<action name="download" class="com.xxx.DownAction">
<result name="success" type="stream">
<param name="contentType">text/plain</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">attachment;filename={fileName}</param>
<param name="bufferSize">1024</param>
</result>
</action>
然而,当我使用jQuery调用动作时:
$.post(
"/download.action",{
para1:value1,
para2:value2
....
},function(data){
console.info(data);
}
);
在Firebug中,我看到数据是用二进制流检索的。我想知道如何打开文件下载窗口,用户可以在本地保存文件?
好的,基于ndpu的代码这里有一个改进的(我认为)ajax_download;-
function ajax_download(url, data) {
var $iframe,
iframe_doc,
iframe_html;
if (($iframe = $('#download_iframe')).length === 0) {
$iframe = $("<iframe id='download_iframe'" +
" style='display: none' src='about:blank'></iframe>"
).appendTo("body");
}
iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
if (iframe_doc.document) {
iframe_doc = iframe_doc.document;
}
iframe_html = "<html><head></head><body><form method='POST' action='" +
url +"'>"
Object.keys(data).forEach(function(key){
iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";
});
iframe_html +="</form></body></html>";
iframe_doc.open();
iframe_doc.write(iframe_html);
$(iframe_doc).find('form').submit();
}
像这样使用这个;-
$('#someid').on('click', function() {
ajax_download('/download.action', {'para1': 1, 'para2': 2});
});
参数被作为适当的post params发送,就像来自一个输入,而不是像前面的例子一样作为一个json编码的字符串。
注意:要警惕在这些表单上可能出现的变量注入。可能有一种更安全的方式来编码这些变量。或者考虑逃离它们。
我尝试下载一个CSV文件,然后在下载完成后做一些事情。所以我需要实现一个适当的回调函数。
使用window.location="…"不是一个好主意,因为下载完成后我无法操作该程序。像这样,改变头,所以这不是一个好主意。
fetch是一个很好的选择,但是它不支持ie11。window.URL.createObjectURL不支持ie11。你可以参考这个。
这是我的密码,它类似于沙赫鲁克·阿拉姆的密码。但你应该注意window。url。createobjecturl可能会造成内存泄漏。你可以参考这个。当响应到达时,数据将被存储到浏览器的内存中。因此,在单击链接之前,文件已经下载。这意味着下载后你可以做任何事情。
$.ajax({
url: 'your download url',
type: 'GET',
}).done(function (data, textStatus, request) {
// csv => Blob
var blob = new Blob([data]);
// the file name from server.
var fileName = request.getResponseHeader('fileName');
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else { // for others
var url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
//Do something after download
...
}
}).then(after_download)
}
我也遇到过同样的问题,并成功地解决了它。我的用例是这样的。
“将JSON数据发送到服务器并接收一个excel文件。
该excel文件由服务器创建,并作为响应返回给客户机。在浏览器中下载具有自定义名称的响应文件”
$("#my-button").on("click", function(){
// Data to post
data = {
ids: [1, 2, 3, 4, 5]
};
// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
var a;
if (xhttp.readyState === 4 && xhttp.status === 200) {
// Trick for making downloadable link
a = document.createElement('a');
a.href = window.URL.createObjectURL(xhttp.response);
// Give filename you wish to download
a.download = "test-file.xls";
a.style.display = 'none';
document.body.appendChild(a);
a.click();
}
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});
上面的代码片段只是执行以下操作
使用XMLHttpRequest将数组作为JSON发送到服务器。
在获取内容作为一个blob(二进制)后,我们正在创建一个可下载的URL,并将其附加到不可见的“a”链接,然后单击它。我在这里做了一个POST请求。相反,您也可以使用简单的GET。我们不能通过Ajax下载文件,必须使用XMLHttpRequest。
这里我们需要小心地在服务器端设置一些东西。我在Python Django HttpResponse中设置了几个头。如果使用其他编程语言,则需要相应地设置它们。
# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
由于我在这里下载xls(excel),我将contentType调整为1以上。您需要根据您的文件类型设置它。您可以使用这种技术下载任何类型的文件。
我的方法完全基于jQuery。对我来说,问题是它必须是一个POST-HTTP调用。我希望它是由jQuery单独完成。
解决方案:
$.ajax({
type: "POST",
url: "/some/webpage",
headers: {'X-CSRF-TOKEN': csrfToken},
data: additionalDataToSend,
dataType: "text",
success: function(result) {
let blob = new Blob([result], { type: "application/octetstream" });
let a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = "test.xml";;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(a.href);
...
},
error: errorDialog
});
解释:
我和很多人所做的是在网页上创建一个链接,指出应该下载目标,并将http-request的结果作为目标。之后,我将链接附加到文档,而不是简单地单击链接,然后删除链接。你不再需要iframe了。
神奇之处在于线条
let blob = new Blob([result], { type: "application/octetstream" });
a.href = window.URL.createObjectURL(blob);
The interesting point is that this solution is only working with a "blob". As you can see in other answers, some are simply using a blob but not explaining why and how to create it.
As you can read e.g. in the Mozilla developer documentation you need a file, media ressource or blob for the function "createObjectURL()" to work. The problem is that your http-response might not be any of those.
Therefore the first thing you must do is to convert your response to a blob. This is what the first line does. Then you can use the "createObjectURL" with your newly created blob.
If you than click the link your browser will open a file-save dialog and you can save your data. Obviously it s possible that you cannot define a fixed filename for your file to download. Then you must make your response more complex like in the answer from Luke.
不要忘记释放内存,特别是在处理大文件时。要获得更多的示例和信息,您可以查看JS blob对象的详细信息