我在服务器端有一个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中,我看到数据是用二进制流检索的。我想知道如何打开文件下载窗口,用户可以在本地保存文件?
我尝试下载一个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)
}
这是使用MVC时的工作代码,你从控制器中获取文件
假设你有你的字节数组声明和填充,你唯一需要做的是使用文件函数(使用System.Web.Mvc)
byte[] bytes = .... insert your bytes in the array
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe");
然后,在同一个控制器中,添加这两个函数
protected override void OnResultExecuting(ResultExecutingContext context)
{
CheckAndHandleFileResult(context);
base.OnResultExecuting(context);
}
private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload";
/// <summary>
/// If the current response is a FileResult (an MVC base class for files) then write a
/// cookie to inform jquery.fileDownload that a successful file download has occured
/// </summary>
/// <param name="context"></param>
private void CheckAndHandleFileResult(ResultExecutingContext context)
{
if (context.Result is FileResult)
//jquery.fileDownload uses this cookie to determine that a file download has completed successfully
Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" });
else
//ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload
if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null)
Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1);
}
然后你将能够调用你的控制器来下载并获得"success"或"failure"回调
$.fileDownload(mvcUrl('name of the controller'), {
httpMethod: 'POST',
successCallback: function (url) {
//insert success code
},
failCallback: function (html, url) {
//insert fail code
}
});
我的方法完全基于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对象的详细信息
我尝试下载一个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)
}
使用窗口。打开https://developer.mozilla.org/en-US/docs/Web/API/Window/open
例如,你可以把这行代码放在点击处理程序中:
window.open('/file.txt', '_blank');
它将打开一个新选项卡(因为'_blank' window-name),该选项卡将打开URL。
你的服务器端代码也应该是这样的:
res.set('Content-Disposition', 'attachment; filename=file.txt');
通过这种方式,浏览器应该提示用户将文件保存到磁盘,而不仅仅是向他们显示文件。它还会自动关闭刚刚打开的选项卡。