通过jQuery.Ajax下载文件

hgu*_*ser 391 javascript ajax jquery jsp download

我在服务器端有一个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>
Run Code Online (Sandbox Code Playgroud)

但是当我使用jQuery调用动作时:

$.post(
  "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*ner 640

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

我创建了jQuery文件下载,它允许使用OnSuccess和OnFailure回调完成文件下载的"类似Ajax"体验,以提供更好的用户体验.看看我的博客文章,了解插件解决的常见问题以及使用它的一些方法,以及jQuery文件下载演示.这是来源

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

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!'));
Run Code Online (Sandbox Code Playgroud)

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

  • 我喜欢你构建的内容,但我怀疑为了获得更多的StackOverFlow功能,你的答案应该包含更多细节.特别是你如何解决问题. (68认同)
  • 我同意,博客是一个更好的地方,可以详细描述如何使用您的插件及其工作原理.但你至少可以简要介绍一下这个插件如何解决这个问题.例如,这可以通过让服务器设置cookie并让你的javascript持续查找cookie直到它存在来解决问题.一旦存在,我们可以假设下载完成.通过这种信息,人们可以很快地轻松推出自己的解决方案,而且答案不再依赖于您的博客/插件/ jquery,并且可以应用于其他库. (26认同)
  • 如果你准确地提到这个"插件"如何绕过限制,而不是强迫我们去你的博客/插件来看它,那就太好了.例如,它是否发布到iframe?是否需要远程脚本保存文件并返回一个URL? (14认同)
  • @asgerhallas当然,但如果说链接消失那就完全没用了. (2认同)
  • Royi,据我了解,AJAX *永远不会*支持文件下载,从而导致文件下载弹出窗口保存到磁盘。你有没有发现我不知道的方法? (2认同)

blu*_*ish 211

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

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

window.location="download.action?para1=value1...."
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这要求服务器将Content-Disposition标头值设置为"attachment",否则浏览器将重定向到(并显示)响应内容 (44认同)
  • 或者使用`window.open(<url>,'_ blank');`来确保下载不会替换当前的浏览器内容(无论Content-Disposition标头如何). (18认同)
  • 很好...因为我正在努力处理下载文件提示和使用jquery ajax ..这个解决方案对我来说很完美.. + 1 (4认同)
  • 此解决方案的问题是,如果操作失败/服务器返回错误,您的页面将被重定向到错误页面.要解决这个问题,请使用iFrame解决方案 (4认同)
  • 这是一个很好的解决方案,但我想在下载时显示加载栏.这是不可能用相同的机制. (2认同)
  • 这个解决方案的真正问题-问题是关于`POST`请求​​。 (2认同)

Luk*_*nga 34

你可以使用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

这意味着我可以执行以下操作以通过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);
}
Run Code Online (Sandbox Code Playgroud)

函数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});
};
Run Code Online (Sandbox Code Playgroud)

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


And*_*ios 26

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

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

最好将<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>
Run Code Online (Sandbox Code Playgroud)

在上述情况下,您需要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...
Run Code Online (Sandbox Code Playgroud)

使用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>
Run Code Online (Sandbox Code Playgroud)

3.框架不可知(/ Struts2框架):Servlet(/ Action)在浏览器内打开文件

如果要在浏览器中打开文件而不是下载它,则必须将Content-disposition设置为inline,但目标不能是当前窗口位置; 你必须定位一个由javascript创建的新窗口,一个<iframe>在页面中,或者使用"讨论"的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>
Run Code Online (Sandbox Code Playgroud)

  • 先生,您的输入:“ Content-Disposition”,“ inline; ....节省了可怜的编码人员的日子:) (2认同)

ndp*_*dpu 23

我创建了一个小功能作为解决方案解决方案(受@JohnCulviner插件启发):

// 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();
}
Run Code Online (Sandbox Code Playgroud)

点击事件演示:

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2}, 'dataname');
});
Run Code Online (Sandbox Code Playgroud)


Joã*_*cos 20

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

 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();
 }
Run Code Online (Sandbox Code Playgroud)

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

  • IE处理的更好版本[link](/sf/answers/3477206981/) (6认同)
  • 谢谢,我使用了这个解决方案.工作就像一个魅力.此外,如果您没有从响应中获取blob,只需创建一个新的Blob. (3认同)

Sha*_*yne 15

好吧,基于ndpu的代码下面有一个改进的(我认为)ajax_download的版本; -

function ajax_download(url, data) {
    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 +"'>" 

    Object.keys(data).forEach(function(key){
        iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";

    });

        iframe_html +="</form></body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}
Run Code Online (Sandbox Code Playgroud)

像这样用它; -

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2});
});
Run Code Online (Sandbox Code Playgroud)

params作为正确的post params发送,好像来自输入而不是像前面的例子那样作为json编码的字符串.

CAVEAT:对这些形式的可变注射潜力保持警惕.可能有一种更安全的方法来编码这些变量.或者考虑逃避它们.


Nar*_*ula 15

我遇到了同样的问题并成功解决了它.我的用例是这样的.

"将JSON数据发布到服务器并接收excel文件.该excel文件由服务器创建并作为对客户端的响应返回.在浏览器中将该响应下载为具有自定义名称的文件 "

$("#my-button").on("click", function(){

// Data to post
data = {
    ids: [1, 2, 3, 4, 5]
};

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});
Run Code Online (Sandbox Code Playgroud)

上面的代码片段正在执行以下操作

  • 使用XMLHttpRequest将数组作为JSON发布到服务器.
  • 在将内容作为blob(二进制)获取后,我们创建了一个可下载的URL并将其附加到不可见的"a"链接,然后单击它.我在这里做了一个POST请求.相反,你也可以进行简单的GET.我们无法通过Ajax下载文件,必须使用XMLHttpRequest.

在这里,我们需要在服务器端仔细设置一些东西.我在Python Django HttpResponse中设置了几个标题.如果使用其他编程语言,则需要相应地设置它们.

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
Run Code Online (Sandbox Code Playgroud)

由于我在这里下载xls(excel),我将contentType调整为高于1.您需要根据文件类型进行设置.您可以使用此技术下载任何类型的文件.


M46*_*M46 14

我的方法完全基于 jQuery。对我来说,问题是它必须是 POST-HTTP 调用。我希望它能够由 jQuery 单独完成。

解决方案:

$.ajax({
    type: "POST",
    url: "/some/webpage",
    headers: {'X-CSRF-TOKEN': csrfToken},
    data: additionalDataToSend,
    dataType: "text",
    success: function(result) {
        let blob = new Blob([result], { type: "application/octetstream" }); 

        let a = document.createElement('a');
        a.href = window.URL.createObjectURL(blob);
        a.download = "test.xml";;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        window.URL.revokeObjectURL(a.href);
                        
        ...
    },
    error: errorDialog
});
Run Code Online (Sandbox Code Playgroud)

解释:

我和其他许多人所做的就是在网页上创建一个链接,指示应该下载目标并将http请求的结果作为目标。之后,我将链接附加到文档,而不是简单地单击链接并随后删除链接。您不再需要 iframe。

神奇之处就在于线条

let blob = new Blob([result], { type: "application/octetstream" }); 
a.href = window.URL.createObjectURL(blob);
Run Code Online (Sandbox Code Playgroud)

有趣的是,该解决方案仅适用于“ blob ”。正如您在其他答案中看到的,有些答案只是使用 blob,但没有解释为什么以及如何创建它。正如您可以在Mozilla 开发人员文档中读到的那样,您需要一个文件、媒体资源或 blob 才能使“ createObjectURL() ”函数正常工作。问题是您的 http 响应可能不是其中任何一个。因此,您必须做的第一件事是将响应转换为 blob。这就是第一行的作用。然后,您可以将“ createObjectURL ”与新创建的 blob 一起使用。如果您单击该链接,您的浏览器将打开一个文件保存对话框,您可以保存数据。显然,您可能无法为要下载的文件定义固定的文件名。那么你必须让你的回答更加复杂,就像卢克的回答一样。

并且不要忘记释放内存,尤其是在处理大文件时。更多示例和信息可以查看JS blob 对象的详细信息


man*_*v07 8

这是我做的,纯粹的javascript和HTML.没有测试它,但这应该适用于所有浏览器.

Javascript函数

var iframe = document.createElement('iframe');
iframe.id = "IFRAMEID";
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro);
iframe.addEventListener("load", function () {
     console.log("FILE LOAD DONE.. Download should start now");
});
Run Code Online (Sandbox Code Playgroud)

仅使用所有浏览器支持的组件,无需其他库.

在此输入图像描述 在此输入图像描述

这是我的服务器端JAVA Spring控制器代码.

@RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET)
public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1,
    HttpServletRequest request, HttpServletResponse response)
            throws ParseException {

    Workbook wb = service.getWorkbook(param1);
    if (wb != null) {
        try {
            String fileName = "myfile_" + sdf.format(new Date());
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
            wb.write(response.getOutputStream());
            response.getOutputStream().close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    }
Run Code Online (Sandbox Code Playgroud)


小智 6

HTML 代码:

<button type="button" id="GetFile">Get File!</button>
Run Code Online (Sandbox Code Playgroud)

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);
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

  • 它给了我 -&gt; VM2821:81 Uncaught TypeError: 无法在“URL”上执行“createObjectURL”:重载解析失败 (3认同)

小智 6

AJAX接收文件后如何下载文件

文件创建时间长,需要显示PRELOADER的时候很方便

提交 Web 表单时的示例:

<script>
$(function () {
    $('form').submit(function () {
        $('#loader').show();
        $.ajax({
            url: $(this).attr('action'),
            data: $(this).serialize(),
            dataType: 'binary',
            xhrFields: {
                'responseType': 'blob'
            },
            success: function(data, status, xhr) {
                $('#loader').hide();
                // if(data.type.indexOf('text/html') != -1){//If instead of a file you get an error page
                //     var reader = new FileReader();
                //     reader.readAsText(data);
                //     reader.onload = function() {alert(reader.result);};
                //     return;
                // }
                var link = document.createElement('a'),
                    filename = 'file.xlsx';
                // if(xhr.getResponseHeader('Content-Disposition')){//filename 
                //     filename = xhr.getResponseHeader('Content-Disposition');
                //     filename=filename.match(/filename="(.*?)"/)[1];
                //     filename=decodeURIComponent(escape(filename));
                // }
                link.href = URL.createObjectURL(data);
                link.download = filename;
                link.click();
            }
        });
        return false;
    });
});
</script>
Run Code Online (Sandbox Code Playgroud)

可选的函数被注释掉以简化示例。

无需在服务器上创建临时文件。

在 jQuery v2.2.4 上OK。旧版本会报错:

Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').
Run Code Online (Sandbox Code Playgroud)


EL *_*bib 5

function downloadURI(uri, name) 
{
    var link = document.createElement("a");
    link.download = name;
    link.href = uri;
    link.click();
}
Run Code Online (Sandbox Code Playgroud)

  • 只是一个警告:Safari 和 IE 不支持 `download` 属性,因此您的文件最终将具有名称“未知” (2认同)

kya*_*kya 5

我尝试下载一个 CSV 文件,然后在下载完成后做一些事情。所以我需要实现一个适当的callback功能。

使用window.location="..."不是一个好主意,因为我下载完成后无法运行程序。像这样的东西,改变标题所以这不是一个好主意。

fetch是一个不错的选择,但它不能支持 IE 11。并且window.URL.createObjectURL不能支持 IE 11。你可以参考这个

这是我的代码,它类似于 Shahrukh Alam 的代码。但是您应该注意window.URL.createObjectURL可能会造成内存泄漏。你可以参考这个。当响应到达时,数据将存储到浏览器的内存中。因此,在您单击a链接之前,文件已下载。这意味着您可以在下载后做任何事情。

$.ajax({
    url: 'your download url',
    type: 'GET',
}).done(function (data, textStatus, request) {
    // csv => Blob
    var blob = new Blob([data]);

    // the file name from server.
    var fileName = request.getResponseHeader('fileName');

    if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
    window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else { // for others
    var url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.style.display = 'none';
    a.href = url;
    a.download = fileName;
    document.body.appendChild(a);
    a.click();
    window.URL.revokeObjectURL(url);

    //Do something after download 
    ...

    }
}).then(after_download)
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

684565 次

最近记录:

6 年,2 月 前