我在服务器端有一个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调用来实现这一点。

然而,有一个变通办法。

步骤:

如果你使用form.submit()下载文件,你可以这样做:

创建一个从客户端到服务器的ajax调用,并将文件流存储在会话中。 当从服务器返回“成功”时,调用form.submit()来处理存储在会话中的文件流。

当你想要决定是否需要在制作form.submit()后下载文件时,这是有帮助的,例如:在form.submit()上,在服务器端发生异常而不是崩溃的情况下,你可能需要在客户端显示一个自定义消息,在这种情况下,这个实现可能会有帮助。

其他回答

1. 框架不可知:Servlet下载文件作为附件

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>

2. Struts2框架:动作下载文件作为附件

<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>

最好使用<s:a>标记,用OGNL指向用<s: URL >标记创建的URL:

<!-- without JS, with Struts tags: THE RIGHT WAY -->    
<s:url action="downloadAction.action" var="url">
    <s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>

在上述情况下,您需要将Content-Disposition报头写入响应,指定文件需要下载(附件),而不是由浏览器打开(内联)。您还需要指定Content Type,并且您可能希望添加文件名和长度(以帮助浏览器绘制一个真实的进度条)。

例如,当下载ZIP文件时:

response.setContentType("application/zip");
response.addHeader("Content-Disposition", 
                   "attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...

使用Struts2(除非您将Action用作Servlet,例如,用于直接流的黑客),您不需要直接向响应写入任何内容;简单地使用Stream结果类型并在struts.xml中配置它就可以了

<result name="success" type="stream">
   <param name="contentType">application/zip</param>
   <param name="contentDisposition">attachment;filename="${fileName}"</param>
   <param name="contentLength">${fileLength}</param>
</result>

3.框架不可知(/ Struts2框架):Servlet(/Action)在浏览器中打开文件

如果你想在浏览器中打开文件,而不是下载它,Content-disposition必须设置为内联,但目标不能是当前窗口位置;你必须目标一个由javascript创建的新窗口,一个<iframe>在页面中,或者一个新窗口创建的飞行与"discussed" target="_blank":

<!-- From a parent page into an IFrame without javascript -->   
<a href="downloadServlet?param1=value1" target="iFrameName">
    download
</a>

<!-- In a new window without javascript --> 
<a href="downloadServlet?param1=value1" target="_blank">
    download
</a>

<!-- In a new window with javascript -->    
<a href="javascript:window.open('downloadServlet?param1=value1');" >
    download
</a>

我创建了一个小函数作为解决方案(灵感来自@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');
});

I found a fix that while it's not actually using ajax it does allow you to use a javascript call to request the download and then get a callback when the download actually starts. I found this helpful if the link runs a server side script that takes a little bit to compose the file before sending it. so you can alert them that it's processing, and then when it does finally send the file remove that processing notification. which is why I wanted to try to load the file via ajax to begin with so that I could have an event happen when the file is requested and another when it actually starts downloading.

js在首页

function expdone()
{
    document.getElementById('exportdiv').style.display='none';
}
function expgo()
{
   document.getElementById('exportdiv').style.display='block';
   document.getElementById('exportif').src='test2.php?arguments=data';
}

iframe

<div id="exportdiv" style="display:none;">
<img src="loader.gif"><br><h1>Generating Report</h1>
<iframe id="exportif" src="" style="width: 1px;height: 1px; border:0px;"></iframe>
</div>

然后是另一个文件:

<!DOCTYPE html>
<html>
<head>
<script>
function expdone()
{
    window.parent.expdone();
}
</script>
</head>
<body>
<iframe id="exportif" src="<?php echo "http://10.192.37.211/npdtracker/exportthismonth.php?arguments=".$_GET["arguments"]; ?>"></iframe>
<script>document.getElementById('exportif').onload= expdone;</script>
</body></html>

我认为有一种方法可以使用js读取数据,这样就不需要php了。但我不知道它的手,我正在使用的服务器支持php,所以这对我有用。我想分享一下,也许能帮到别人。

使用窗口。打开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');

通过这种方式,浏览器应该提示用户将文件保存到磁盘,而不仅仅是向他们显示文件。它还会自动关闭刚刚打开的选项卡。

function downloadURI(uri, name) 
{
    var link = document.createElement("a");
    link.download = name;
    link.href = uri;
    link.click();
}