关于javascript:通过jQuery.Ajax下载文件

Download a file by jQuery.Ajax

我在服务器端有一个Struts2动作用于文件下载。

1
2
3
4
5
6
7
    <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调用动作时:

1
2
3
4
5
6
7
8
9
$.post(
 "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);

在Firebug中我看到使用二进制流检索数据。 我想知道如何打开用户可以在本地保存文件的文件下载窗口?


2019年现代浏览器更新

这是我现在推荐的方法,但有一些注意事项:

  • 需要一个相对现代的浏览器
  • 如果文件预计会非常大,您应该做一些类似于原始方法(iframe和cookie)的事情,因为以下某些操作可能会消耗系统内存,至少与正在下载的文件和/或其他有趣的CPU一样大副作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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文件下载,它允许使用OnSuccess和OnFailure回调完成文件下载的"类似Ajax"体验,以提供更好的用户体验。看看我的博客文章,了解插件解决的常见问题以及使用它的一些方法,以及jQuery文件下载的演示。这是来源

这是一个使用带有promises的插件源的简单用例演示。该演示页面还包含许多其他"更好的用户体验"示例。

1
2
3
$.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方法更明确的控制。


没有人发布这个@Pekka的解决方案......所以我会发布它。它可以帮助某人。

您不需要通过Ajax执行此操作。只是用

1
window.location="download.action?para1=value1...."


你可以使用HTML5

注意:返回的文件数据必须是base64编码的,因为你不能JSON编码二进制数据

在我的AJAX响应中,我有一个如下所示的数据结构:

1
2
3
4
5
6
7
8
{
    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保存文件

1
2
3
4
5
6
7
8
9
10
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取自此处,必须按照此函数使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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});
};

如果您的服务器正在转储要保存的filedata,这很好。但是,我还没有弄清楚如何实现HTML4后备


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

1
2
3
4
5
6
7
<!-- with JS -->

    download


<!-- without JS -->
download

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

1
2
3
4
5
6
7
<!-- with JS -->

    download


<!-- without JS -->
download

最好使用带有OGNL的标记指向使用标记创建的URL:

1
2
3
4
5
<!-- 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标头写入响应,指定需要下载文件(attachment)而不是浏览器打开文件(inline)。您还需要指定内容类型,并且可能需要添加文件名和长度(以帮助浏览器绘制真实的进度条)。

例如,下载ZIP时:

1
2
3
4
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中配置它就可以了:例子

1
2
3
4
5
<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设置为inline,但目标不能是当前窗口位置;你必须定位一个由javascript创建的新窗口,页面中的