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


当前回答

在上面添加一些东西来下载文件

下面是一些生成字节数组的java spring代码

@RequestMapping(value = "/downloadReport", method = { RequestMethod.POST })
    public ResponseEntity<byte[]> downloadReport(
            @RequestBody final SomeObejct obj, HttpServletResponse response) throws Exception {

        OutputStream out = new ByteArrayOutputStream();
        // write something to output stream
        HttpHeaders respHeaders = new HttpHeaders();
        respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        respHeaders.add("X-File-Name", name);
        ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
        return new ResponseEntity<byte[]>(bos.toByteArray(), respHeaders, HttpStatus.CREATED);
    }

现在在javascript代码中使用filesver .js,可以下载带有下面代码的文件

var json=angular.toJson("somejsobject");
var url=apiEndPoint+'some url';
var xhr = new XMLHttpRequest();
//headers('X-File-Name')
xhr.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 201) {
        var res = this.response;
        var fileName=this.getResponseHeader('X-File-Name');
        var data = new Blob([res]);
        saveAs(data, fileName); //this from FileSaver.js
    }
}    
xhr.open('POST', url);
xhr.setRequestHeader('Authorization','Bearer ' + token);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'arraybuffer';
xhr.send(json);

以上将下载文件

其他回答

如果你想使用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()

好的,基于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();
}

像这样使用这个;-

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

参数被作为适当的post params发送,就像来自一个输入,而不是像前面的例子一样作为一个json编码的字符串。

注意:要警惕在这些表单上可能出现的变量注入。可能有一种更安全的方式来编码这些变量。或者考虑逃离它们。

如果服务器在响应中写入文件(包括cookie) 你使用它们来确定文件下载是否开始),简单地创建一个带有值的表单并提交它:

function ajaxPostDownload(url, data) {
    var $form;
    if (($form = $('#download_form')).length === 0) {
        $form = $("<form id='download_form'" + " style='display: none; width: 1px; height: 1px; position: absolute; top: -10000px' method='POST' action='" + url + "'></form>");
        $form.appendTo("body");
    }
    //Clear the form fields
    $form.html("");
    //Create new form fields
    Object.keys(data).forEach(function (key) {
        $form.append("<input type='hidden' name='" + key + "' value='" + data[key] + "'>");
    });
    //Submit the form post
    $form.submit();
}

用法:

ajaxPostDownload('/fileController/ExportFile', {
    DownloadToken: 'newDownloadToken',
    Name: $txtName.val(),
    Type: $txtType.val()
});

控制器方法:

[HttpPost]
public FileResult ExportFile(string DownloadToken, string Name, string Type)
{
    //Set DownloadToken Cookie.
    Response.SetCookie(new HttpCookie("downloadToken", DownloadToken)
    {
        Expires = DateTime.UtcNow.AddDays(1),
        Secure = false
    });

    using (var output = new MemoryStream())
    {
        //get File
        return File(output.ToArray(), "application/vnd.ms-excel", "NewFile.xls");
    }
}

没有人贴出这个@Pekka的解决方案…所以我会把它贴出来。它可以帮助别人。

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

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

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

使用window.location="…"不是一个好主意,因为下载完成后我无法操作该程序。像这样,改变头,所以这不是一个好主意。

fetch是一个很好的选择,但是它不支持ie11。window.URL.createObjectURL不支持ie11。你可以参考这个。

这是我的密码,它类似于沙赫鲁克·阿拉姆的密码。但你应该注意window。url。createobjecturl可能会造成内存泄漏。你可以参考这个。当响应到达时,数据将被存储到浏览器的内存中。因此,在单击链接之前,文件已经下载。这意味着下载后你可以做任何事情。

$.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)
}