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


当前回答

使用窗口。打开https://developer.mozilla.org/en-US/docs/Web/API/Window/open

例如,你可以把这行代码放在点击处理程序中:

window.open('/file.txt', '_blank');

它将打开一个新选项卡(因为'_blank' window-name),该选项卡将打开URL。

你的服务器端代码也应该是这样的:

res.set('Content-Disposition', 'attachment; filename=file.txt');

通过这种方式,浏览器应该提示用户将文件保存到磁盘,而不仅仅是向他们显示文件。它还会自动关闭刚刚打开的选项卡。

其他回答

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

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

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

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

我为这个问题挣扎了很长一段时间。最后,这里建议的一个优雅的外部库帮助了我。

如果你想使用jQuery文件下载,请注意这一点。 您需要重新设置响应,否则将无法下载

    //The IE will only work if you reset response
    getServletResponse().reset();
    //The jquery.fileDownload needs a cookie be set
    getServletResponse().setHeader("Set-Cookie", "fileDownload=true; path=/");
    //Do the reset of your action create InputStream and return

您的操作可以实现ServletResponseAware来访问getServletResponse()

如何下载一个文件后收到它的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>

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

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

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').