在Ajax中从服务器响应获取Excel文件(.xlsx)

11

在通过成功的ajax方法得到响应后,我无法获取Excel文件并在浏览器中打开下载窗口。 我有适当的Content-Type和Content-Disposition标头,我尝试在js中使用Blob,但是我无法实现我想要的简单文件下载。
我完成了几个版本的ajax,其中之一如下所示。 我开发了可以返回Excel文件的ajax,但我无法正确打开它,因为它已损坏(尽管具有.xlsx扩展名)。

也许问题出在Blob构造函数中使用的不适当数据类型?

我尝试使用“xhr.response”而不是来自success方法参数的“data”,但这也不起作用。 我在Chrome的开发人员工具中检查了响应标头,它们被正确设置了。
重要的事情是,服务器端创建的所有Excel工作簿都是正确的,因为在以前的版本中,数据是通过URL发送的,而不是在ajax post中发送的。

以下是Java / Spring服务器端的控制器方法:

response.reset();
response.setContentType("application/vnd.ms-excel");
response.addHeader("Content-Disposition","attachment;filename=\"" + className + " " +  title + ".xlsx\"");
    try (ServletOutputStream output = response.getOutputStream()){
        workbook.write(output);
        output.flush();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

我的 Ajax 下载文件并打开下载窗口:

$.ajax({
    url: myUrl,
    type: 'POST',
    data: myData,
    success: function(data, status, xhr) {
        var contentType = 'application/vnd.ms-excel';

        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }
        console.log("FILENAME: " + filename);

        try {
            var blob = new Blob([data], { type: contentType });

            var downloadUrl = URL.createObjectURL(blob);
            var a = document.createElement("a");
            a.href = downloadUrl;
            a.download = filename;
            document.body.appendChild(a);
            a.click();

        } catch (exc) {
            console.log("Save Blob method failed with the following exception.");
            console.log(exc);
        }
4个回答

21

看起来JQuery在处理响应中的二进制数据方面遇到了一些问题。我仅使用XMLHttpRequest,并将所有数据添加到URL中。

var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.responseType = 'blob';

request.onload = function(e) {
    if (this.status === 200) {
        var blob = this.response;
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, fileName);
        }
        else{
            var downloadLink = window.document.createElement('a');
            var contentTypeHeader = request.getResponseHeader("Content-Type");
            downloadLink.href = window.URL.createObjectURL(new Blob([blob], { type: contentTypeHeader }));
            downloadLink.download = fileName;
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.body.removeChild(downloadLink);
           }
       }
   };
   request.send();

哦,我的天啊,非常感谢你提供的答案。你真是最棒的! - Sol
第16行的fileName未被定义。你在哪里定义它? - Möoz
1
@Möoz 这已经设置好了。但在这种情况下并不重要,因为它可以是任何东西,比如"My_File.xlsx"。 - KamilosD
我能够下载文件,但是当我打开它时,它会显示“抱歉,我们找不到 C:\mypath[object Object],[object Object],....[object Ob.xls。它可能被移动、重命名或删除了吗?”此外,在下载期间是否有任何更改文件名称的方法? - Diamond King

4

在尝试多种方法从带有Unicode内容的Web API获取Excel文件后,最终这段代码对我起了作用:

$.ajax({
                type: 'GET',
                cache: false,
                url: "https://localhost:44320/WeatherForecast",
              
                xhrFields: {
                    // make sure the response knows we're expecting a binary type in return.
                    // this is important, without it the excel file is marked corrupted.
                    responseType: 'arraybuffer'
                }
            })
                .done(function (data, status, xmlHeaderRequest) {
                    var downloadLink = document.createElement('a');
                    var blob = new Blob([data],
                        {
                            type: xmlHeaderRequest.getResponseHeader('Content-Type')
                        });
                    var url = window.URL || window.webkitURL;
                    var downloadUrl = url.createObjectURL(blob);
                    var fileName = '';

                  

                    if (typeof window.navigator.msSaveBlob !== 'undefined') {
                        window.navigator.msSaveBlob(blob, fileName);
                    } else {
                        if (fileName) {
                            if (typeof downloadLink.download === 'undefined') {
                                window.location = downloadUrl;
                            } else {
                                downloadLink.href = downloadUrl;
                                downloadLink.download = fileName;
                                document.body.appendChild(downloadLink);
                                downloadLink.click();
                            }
                        } else {
                            window.location = downloadUrl;
                        }

                        setTimeout(function () {
                            url.revokeObjectURL(downloadUrl);
                        },
                            100);
                    }
                });


1
我们最近也遇到了完全相同的问题。当我们将responseType: 'arraybuffer'添加到ajax参数中时,它开始工作。而且最好使用库https://github.com/eligrey/FileSaver.js/而不是手动点击链接,因为这个工具也会撤销内存。

我已经尝试了 responseType: 'arraybuffer',但它失败了,因为不适当的 responseType 可能是 text。但我通过直接创建 XMLHttpRequest 解决了这个问题。下面的评论中有可运行的代码。 - KamilosD

0
在我的情况下,这个邮政编码工作正常。

$('.btnExportToExcel').click(async (e) => {
    e.preventDefault();
    const info = JSON.parse(e.currentTarget.dataset.info);
    let action = 1;
   

    try {
        const data = {
        idCampagna: info.idCampagna,
        fileName: info.fileName,
       };
         this.ShowSpinner("Downloading");

        // this.HideSpinner();

        $.ajax({
            type: 'GET',
            cache: false,
            url: "ExportToExcel",
            data: data,
            xhrFields: {
                // make sure the response knows we're expecting a binary type in return.
                // this is important, without it the excel file is marked corrupted.
                responseType: 'arraybuffer'
            }
        })
            .done(function (data, status, xmlHeaderRequest) {
                var downloadLink = document.createElement('a');
                var blob = new Blob([data],
                    {
                        type: xmlHeaderRequest.getResponseHeader('Content-Type')
                    });
                var url = window.URL || window.webkitURL;
                var downloadUrl = url.createObjectURL(blob);
                var fileName = '' +'.xls';



                if (typeof window.navigator.msSaveBlob !== 'undefined') {
                    window.navigator.msSaveBlob(blob, fileName);
                } else {
                    if (fileName) {
                        if (typeof downloadLink.download === 'undefined') {
                            window.location = downloadUrl;
                        } else {
                            downloadLink.href = downloadUrl;
                            downloadLink.download = fileName;
                            document.body.appendChild(downloadLink);
                            downloadLink.click();
                        }
                    } else {
                        window.location = downloadUrl;
                    }

                    setTimeout(function () {
                        url.revokeObjectURL(downloadUrl);
                    },
                        100);
                }
            });
        this.HideSpinner();
    } catch (error) {
        console.log(error);
    }
});


目前的回答写得不够清楚。请[编辑]以添加更多细节,帮助其他人理解这个回答是如何解决所提出的问题的。您可以在帮助中心找到更多关于如何撰写好回答的信息。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接