使用jQuery AJAX和Spring MVC 3如何从服务器下载文件

14

我希望实现从服务器下载已上传文件的功能(使用AJAX)。在服务器端,我编写了以下代码:

@RequestMapping(value = "/getInvoice/approvalId/{approvalId}", method = RequestMethod.GET)
public
@ResponseBody
byte[] getInvoice(@PathVariable("approvalId") Integer approvalId, HttpServletResponse response) throws IOException {
    String fileName = this.approvalService.getFullInvoicePath(approvalId);
    File file = new File(fileName);

    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
    response.setHeader("Pragma", "no-cache");
    response.setHeader("Cache-Control", "no-cache");
    response.setContentLength((int) file.length());
    return FileUtils.readFileToByteArray(file);
}

Fiddler2 显示响应:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Disposition: attachment; filename="invoice.pdf"
Pragma: no-cache
Cache-Control: no-cache
Content-Type: application/octet-stream;charset=UTF-8
Content-Length: 1028351
Date: Sun, 17 Jul 2011 08:16:41 GMT

%PDF-1.4
%����
6 0 obj <</Linearized 1/L 1028351/O 8/E 1024254/N 1/T 1028185/H [ 5056 544]>>
endobj

xref
6 238 
*** FIDDLER: RawDisplay truncated at 128 characters. Right-click to disable truncation. ***

如何使用jQuery处理并强制浏览器下载文件?

我会参考这个问题:[https://dev59.com/kXI95IYBdhLWcg3wtwVe] - 包括一个jQuery插件来完成这个任务。 - Adam
3个回答

34

通常有两种选项可用,但都不涉及AJAX。而且jQuery也不是很有帮助。

选项1:IFrame

将一个不可见的IFrame放入你的页面中:

<iframe id="downloadFrame" style="display:none"></iframe>

根据您提供的信息(您没有提及如何触发下载),当下载应该开始时,请使用JavaScript(可能还要使用jQuery)设置IFrame的URL。 在您的情况下,URL类似于/getInvoice/approvalId/123

var iframe = document.getElementById("downloadFrame");
iframe .src = "/getInvoice/approvalId/123";

设置IFrame的URL应该触发浏览器呈现下载对话框。

选项2:导航到下载URL

第二个选项更简单。只需导航到下载URL即可。一旦浏览器确定它是无法显示的MIME类型,它将呈现一个下载对话框。

因此,当下载被触发时,请执行以下JavaScript代码:

window.location.href = "/getInvoice/approvalId/123";

注意

我不确定所有浏览器是否可靠地在PDF文件中呈现下载对话框。一些浏览器可能会尝试在浏览器内部显示它。 Content-Disposition HTTP头很有帮助,但并不保证。


我决定不用@ResponseBody返回byte[]。现在,我设置响应头<pre> private void setPdfHeaders(HttpServletResponse response, String shortFileName, File file) { response.setContentType("application/pdf"); response.setHeader("Content-disposition", "attachment; filename=" + shortFileName); response.setHeader("Content-Length", String.valueOf(file.length())); }</pre> 然后将流写入响应的OutputStream,并使用您推荐的选项2。 - Tural
我喜欢iframe选项,但想添加一条注释。当从服务器提供文档时,您需要适当地设置mime类型,否则您将无法获得正确的浏览器对话框交互。例如,我的后端为.xlsx文档设置了"text/plain",但没有成功。 - jomofrodo

4

Codo提供的jQuery化的答案:

   $('#downloadFrame').remove(); // 如果框架不存在,这不应该失败
   $('body').append('<iframe id="downloadFrame" style="display:none"></iframe>');
   $('#downloadFrame').attr('src','/下载链接在此处');

2
也许更好的策略是使用jQuery在页面上构建一个新的<A>链接,该链接将引用相同的URL,并在DOM中适当的位置插入此新元素。完成这个步骤后,也许jQuery甚至可以为用户点击它,启动下载。

很遗憾,出于明显的安全原因,您无法通过编程方式单击链接。 - Matt K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接