有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

javascript通过jQuery下载一个文件。AJAX

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

  1. # 1 楼答案

    我创建了一个小功能作为解决方案(灵感来自@JohnCulviner plugin):

    // 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');
    });
    
  2. # 2 楼答案

    你可以使用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取自here,必须按照此函数使用

    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回退

  3. # 3 楼答案

    2019现代浏览器更新

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

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

    &13; 第13部分,;
    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!'));
    和#13;
    和#13;

    2012基于jQuery/iframe/Cookie的原始方法

    Bluish在这一点上是完全正确的,您不能通过Ajax实现,因为JavaScript无法将文件直接保存到用户的计算机上(出于安全考虑)。不幸的是,将主窗口的URL指向您的文件下载意味着您无法控制文件下载时的用户体验

    我创建了jQuery File Download,它允许“类似Ajax”的文件下载体验,包括OnSuccess和OnFailure回调,以提供更好的用户体验。看看我的blog post关于插件解决的常见问题和一些使用它的方法,还有demo of jQuery File Download in action。这是source

    下面是一个简单的用例演示,使用带有承诺的插件source。{a7}还包括许多其他“更好的用户体验”示例

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

  • # 4 楼答案

    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>
    

    最好使用带有OGNL<s:a>标记指向使用<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>
    

    在上述情况下,需要将内容处置头写入响应,指定文件需要下载(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(除非您将操作用作Servlet,例如hackfor direct streaming),您不需要直接向响应写入任何内容;只需使用Stream result type并在struts中配置它。xml将起作用:EXAMPLE

    <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 Framework):Servlet(/Action)在浏览器中打开文件

    如果要在浏览器中打开文件,而不是下载文件,则必须将内容配置设置为内联,但目标不能是当前窗口位置;您必须以javascript创建的新窗口、页面中的<iframe>或动态创建的新窗口为目标,并使用“讨论”target=“\u 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>
    
  • # 5 楼答案

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

     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();
     }
    

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

  • # 6 楼答案

    Noone发布了这个@Pekka's solution。。。所以我会把它贴出来。它可以帮助某人

    您不需要通过Ajax来实现这一点。只用

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