我在服务器端有一个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>

HTML代码:

<button type="button" id="GetFile">Get File!</button>

jQuery代码:

$('#GetFile').on('click', function () {
    $.ajax({
        url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/172905/test.pdf',
        method: 'GET',
        xhrFields: {
            responseType: 'blob'
        },
        success: function (data) {
            var a = document.createElement('a');
            var url = window.URL.createObjectURL(data);
            a.href = url;
            a.download = 'myfile.pdf';
            document.body.append(a);
            a.click();
            a.remove();
            window.URL.revokeObjectURL(url);
        }
    });
});

这就是它在任何浏览器中都很好(我使用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'));                 

        }

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方法更明确的控制。