我在服务器端有一个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中,我看到数据是用二进制流检索的。我想知道如何打开文件下载窗口,用户可以在本地保存文件?
还有另一种解决方案下载ajax网页。但我指的是必须首先处理然后下载的页面。
首先,您需要将页面处理与结果下载分离。
1) ajax调用中只进行页面计算。
$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },
function(data, status)
{
if (status == "success")
{
/* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
window.location.href = DownloadPage.php+"?ID="+29;
}
}
);
// For example: in the CalculusPage.php
if ( !empty($_POST["calculusFunction"]) )
{
$ID = $_POST["ID"];
$query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
...
}
// For example: in the DownloadPage.php
$ID = $_GET["ID"];
$sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
...
$filename="Export_Data.xls";
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: inline; filename=$filename");
...
我希望这个解决方案对很多人都有用,就像对我一样。
使用窗口。打开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');
通过这种方式,浏览器应该提示用户将文件保存到磁盘,而不仅仅是向他们显示文件。它还会自动关闭刚刚打开的选项卡。
2019现代浏览器更新
这是我现在推荐的方法,但有几点注意事项:
需要一个相对现代的浏览器
如果文件非常大,您可能会采取与原始方法(iframe和cookie)类似的方法,因为以下一些操作可能会消耗至少与正在下载的文件相同大的系统内存和/或其他有趣的CPU副作用。
fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
// the filename you want
a.download = 'todo-1.json';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
alert('your file has downloaded!'); // or you know, something with better UX...
})
.catch(() => alert('oh no!'));
2012年原创jQuery/iframe/Cookie的方法
Bluish在这一点上是完全正确的,你不能通过Ajax做到这一点,因为JavaScript不能直接将文件保存到用户的计算机(出于安全考虑)。不幸的是,将主窗口的URL指向您的文件下载意味着当文件下载发生时,您几乎无法控制用户体验。
我创建了jQuery文件下载,它允许“类似Ajax”的体验,文件下载完成OnSuccess和OnFailure回调,以提供更好的用户体验。看看我的博客文章,关于这个插件解决的常见问题和一些使用它的方法,还有一个jQuery文件下载的演示。这是来源
下面是一个简单的用例演示,使用带有承诺的插件源代码。演示页面还包括许多其他“更好的用户体验”示例。
$.fileDownload('some/file.pdf')
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });
取决于你需要支持什么浏览器,你可以使用https://github.com/eligrey/FileSaver.js/,它允许比jQuery文件下载使用的IFRAME方法更明确的控制。
这就是它在任何浏览器中都很好(我使用asp.net核心)
function onDownload() {
const api = '@Url.Action("myaction", "mycontroller")';
var form = new FormData(document.getElementById('form1'));
fetch(api, { body: form, method: "POST"})
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
$('#linkdownload').attr('download', 'Attachement.zip');
$('#linkdownload').attr("href", url);
$('#linkdownload')
.fadeIn(3000,
function() { });
})
.catch(() => alert('An error occurred'));
}
<button type="button" onclick="onDownload()" class="btn btn-primary btn-sm">Click to Process Files</button>
<a role="button" href="#" style="display: none" class="btn btn-sm btn-secondary" id="linkdownload">Click to download Attachments</a>
<form asp-controller="mycontroller" asp-action="myaction" id="form1"></form>
function onDownload() {
const api = '@Url.Action("myaction", "mycontroller")';
//form1 is your id form, and to get data content of form
var form = new FormData(document.getElementById('form1'));
fetch(api, { body: form, method: "POST"})
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
$('#linkdownload').attr('download', 'Attachments.zip');
$('#linkdownload').attr("href", url);
$('#linkdownload')
.fadeIn(3000,
function() {
});
})
.catch(() => alert('An error occurred'));
}
我创建了一个小函数作为解决方案(灵感来自@JohnCulviner插件):
// creates iframe and form in it with hidden field,
// then submit form with provided data
// url - form url
// data - data to form field
// input_name - form hidden input name
function ajax_download(url, data, input_name) {
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 +"'>" +
"<input type=hidden name='" + input_name + "' value='" +
JSON.stringify(data) +"'/></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}, 'dataname');
});
你可以用HTML5
注意:返回的文件数据必须是base64编码的,因为JSON不能编码二进制数据
在我的AJAX响应中,我有一个看起来像这样的数据结构:
{
result: 'OK',
download: {
mimetype: string(mimetype in the form 'major/minor'),
filename: string(the name of the file to download),
data: base64(the binary data as base64 to download)
}
}
这意味着我可以执行以下操作来通过AJAX保存文件
var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
// Do it the HTML5 compliant way
var blob = base64ToBlob(result.download.data, result.download.mimetype);
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = result.download.filename;
a.click();
window.URL.revokeObjectURL(url);
}
函数base64ToBlob是从这里取的,必须按照这个函数使用
function base64ToBlob(base64, mimetype, slicesize) {
if (!window.atob || !window.Uint8Array) {
// The current browser doesn't have the atob function. Cannot continue
return null;
}
mimetype = mimetype || '';
slicesize = slicesize || 512;
var bytechars = atob(base64);
var bytearrays = [];
for (var offset = 0; offset < bytechars.length; offset += slicesize) {
var slice = bytechars.slice(offset, offset + slicesize);
var bytenums = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
bytenums[i] = slice.charCodeAt(i);
}
var bytearray = new Uint8Array(bytenums);
bytearrays[bytearrays.length] = bytearray;
}
return new Blob(bytearrays, {type: mimetype});
};
如果您的服务器正在转储要保存的文件数据,这很好。然而,我还没有完全弄清楚如何实现HTML4的回退