通过 jQuery.Ajax 下载文件

IT技术 javascript jquery ajax jsp download
2021-01-04 01:30:40

我在服务器端有一个用于文件下载的 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 中,我看到使用二进制流检索数据我想知道如何打开用户可以在本地保存文件文件下载窗口

6个回答

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 File Download,它允许通过 OnSuccess 和 OnFailure 回调完成文件下载的“类似 Ajax”的体验,以提供更好的用户体验。看看我的博客文章,了解插件解决的常见问题和使用它的一些方法,以及jQuery 文件下载演示这是来源

这是一个使用带有 promise的插件的简单用例演示演示页包含了许多其他“更好的UX”的例子也是如此。

$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

根据您需要支持的浏览器,您可以使用https://github.com/eligrey/FileSaver.js/,它允许比 jQuery File Download 使用的 IFRAME 方法更明确的控制。

@asgerhallas 当然,但如果所述链接消失,那完全没用。
2021-02-07 01:30:40
如果您能准确地提及这个“插件”如何绕过限制,而不是强迫我们去您的博客/插件源查看它,那就太好了。例如,它是不是发布到 iframe?是不是要求远程脚本保存文件并返回一个 url 给它?
2021-02-11 01:30:40
我喜欢你构建的东西,但我怀疑要获得更多 StackOverFlow 信用,你在这里的答案应该包含更多细节。具体看你是如何解决问题的。
2021-02-15 01:30:40
Royi,据我所知,AJAX永远无法支持导致文件下载弹出窗口保存到磁盘的文件下载。你有没有找到我不知道的方法?
2021-02-18 01:30:40
我同意,博客是一个更好的地方,可以详细描述如何使用您的插件及其工作原理。但是您至少可以简要介绍一下该插件如何解决问题。例如,这通过让服务器设置 cookie 并让您的 javascript 不断查找 cookie 直到它存在来解决问题。一旦它存在,我们就可以假设下载已经完成。有了这种信息,人们可以很容易地快速推出自己的解决方案,并且答案不再 100% 依赖于您的博客/插件/jquery,并且可以应用于其他库。
2021-02-23 01:30:40

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

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

window.location="download.action?para1=value1...."
或者window.open(<url>, '_blank');用于确保下载不会替换您当前的浏览器内容(无论 Content-Disposition 标头如何)。
2021-02-07 01:30:40
这种解决方案的问题在于,如果操作失败/服务器返回错误,您的页面将被重定向到错误页面。要解决该问题,请使用 iFrame 解决方案
2021-02-11 01:30:40
不错...因为我在处理下载文件提示和使用 jquery ajax 时遇到了困难...而且这个解决方案对我来说非常有效...+1
2021-02-12 01:30:40
这个解决方案的真正问题 - 问题是关于POST请求。
2021-02-19 01:30:40
请注意,这要求服务器将 Content-Disposition 标头值设置为“附件”,否则浏览器将重定向到(并显示)响应内容
2021-03-09 01:30:40

你可以用 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 回退

在某些浏览器中,您可能需要将a加到 dom 中才能使此代码工作和/或删除该revokeObjectURL部分:document.body.appendChild(a)
2021-02-07 01:30:40
a.click()似乎并没有工作在Firefox ...任何想法?
2021-02-10 01:30:40
拯救了我的一天(也可能是一份工作 :) )无论如何都不是 javascript 专家......更多的是 Java 人。但是,我不知道为什么简单的“createObjectURL(new Blob([atob(base64)]))”不起作用!它根本没有,而所有的本能都说它必须。呜呜呜……
2021-02-28 01:30:40
在行var bytechars = atob(base64)它抛出一个错误JavaScript runtime error: InvalidCharacterError我使用的是 Chrome 版本 75.0.3770.142,但我不知道,这里有什么问题。
2021-02-28 01:30:40

让浏览器下载文件的简单方法是发出这样的请求:

 function downloadFile(urlToSend) {
     var req = new XMLHttpRequest();
     req.open("GET", urlToSend, true);
     req.responseType = "blob";
     req.onload = function (event) {
         var blob = req.response;
         var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
         var link=document.createElement('a');
         link.href=window.URL.createObjectURL(blob);
         link.download=fileName;
         link.click();
     };

     req.send();
 }

这将打开浏览器下载弹出窗口。

如果您使用的是 IE11,来自 @startsWith_R 的链接真的很有帮助
2021-02-09 01:30:40
带有 IE 处理链接的更好版本
2021-02-11 01:30:40
谢谢,我使用了这个解决方案。像魅力一样工作。此外,如果您没有从响应中获得 Blob,只需创建一个新 Blob。
2021-02-26 01:30:40

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 Framework:动作下载文件作为附件

<!-- 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 的标记指向使用标记创建URL会更好<s: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标头写入响应,指定文件需要下载 ( attachment) 而不是由浏览器打开 ( inline)。您还需要指定内容类型,并且您可能希望添加文件名和长度(以帮助浏览器绘制逼真的进度条)。

例如,下载 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,例如直接流式传输的 hack ),您不需要直接向响应写入任何内容;只需使用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.Framework agnostic(/Struts2框架):Servlet(/Action)在浏览器内打开文件

如果你想在浏览器中打开文件,而不是下载它,必须将Content-disposition设置为inline,但目标不能是当前窗口位置;您必须定位由 javascript 创建的新窗口、<iframe>页面中的一个或使用“讨论”目标 =“_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>
如果你有很多参数,它不起作用,因为你会得到too long url错误。
2021-02-09 01:30:40
先生,您的输入:“内容处理”、“内联;.... 拯救了可怜的程序员的一天:)
2021-02-17 01:30:40
这是提到“window.open”的唯一答案(其中一条评论提到了它)。
2021-03-08 01:30:40