【问题标题】:How to download XLSX file from a server response in javascript?如何从 JavaScript 中的服务器响应下载 XLSX 文件?
【发布时间】:2020-03-23 08:44:32
【问题描述】:

我有一个使用 Micronaut 框架的反应前端和 java 服务器。在我的 Micronaut 框架的 BaseController 中,我正在创建一个 XLSX 文件,然后发送响应,以便在从前端代码调用时下载它。

基础控制器

@Inject ExportService exportService

@Controller('/abc/api/v1')
@Slf4j
class BaseController implements CachingHandler, CommonUtils {
    @Post('/export/{name}')
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.ALL)
    executeExporter(String name, @Nullable @Body LinkedHashMap payload) {
        def handler = { LinkedHashMap paramMap ->
            List paramMapList = paramMap.get("data") as List
            Byte[] resultBytes = exportService.exportToXLSX(paramMapList, getFileName(getLookupValue(name), paramMap.get("key") as String), true)
            log.info String.format("About to return XLSX file %s for %s",getFileName(getLookupValue(name), paramMap.get("key") as String), name)
            return resultBytes
        }
        def results = handler.call(payload)
        InputStream inputStream = new ByteArrayInputStream(results)
        return new StreamedFile(inputStream, getFileName(getLookupValue(name), payload["key"] as String))
    }
}

导出服务

注意:使用这个类的 main 方法,我可以创建一个TestReport.xlsx 文件。 XLSXExporter 类使用 Apache POI 库生成 XLSX 文件。

@Prototype
@Slf4j
class ExportService implements CommonUtils {

    Byte[] exportToXLSX(List response, String fileName, boolean isDelete) {
        def headers = []
        try {
            headers = response[0].keySet()
        } catch(Exception e) {
            e.printStackTrace()
        }
        Map params = [:]
        params[Constants.HEADERS] = headers
        params[Constants.DATA] = response

        XLSXExporter xlsxExporter = new XLSXExporter()
        boolean fileCreated = xlsxExporter.writeData(Constants.DEFAULT_SHEET_NAME, fileName, params)
        if (fileCreated) {
            Byte[] workbookContent = xlsxExporter.getFile(fileName, isDelete)
            return workbookContent
        } else {
            return new Byte[0]
        }
    }

    static void main(String[] args) {
        def param = []
        for (int i in 1..5) {
            def paramMap = [:]
            paramMap["key1"] = "data"+i
            paramMap["key2"] = "data"+i
            paramMap["key3"] = "data"+i
            param.add(paramMap)
        }
        ExportService exportService = new ExportService()
        Byte[] resultBytes = exportService.exportToXLSX(param, "TestReport.xlsx", false)

        /**
            I am able to create a proper TestReport.xlsx file using this main method
        **/
    }
}

我从 Postman 测试了上述 API,它能够下载 XLSX 文件。

当我使用我的应用程序 (javascript) 调用上述导出 API 时,它能够下载 xlsx 文件但无法打开。

调用导出 API 的 Javascript 代码

const exportData = (filteredRows, activity) => {
    let filename = "TestReport.xlsx";
    return axios({
        headers: {
            'Content-type': 'application/json'
         },
        accept:'application/x-www-form-urlencoded',
        url: '/abc/api/v1/export/'+ activity,
        method: 'post',
        data: {
            "key": "8575",
            "type":"userdetails",
            "data":filteredRows
        }
    }).then(resp => {
        var blob = resp.data;
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, filename);
        }
        else{
            var downloadLink = window.document.createElement('a');
            downloadLink.href = window.URL.createObjectURL(new Blob([blob], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}));
            downloadLink.download = filename;
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.body.removeChild(downloadLink);
           }
    });
}

在上面的对话框中单击“是”后,我会收到以下错误消息,与从 Postman 下载的文件相反,没有显示任何内容。

【问题讨论】:

  • 不是这个问题的直接解决方案,而是实现相同结果的不同方法:而不是 axios 调用并生成<a>,尝试生成一个<form method="post">,输入保存数据并提交它。由于服务器发回一个文件,通过调用submit() 提交表单也应该会触发下载。

标签: javascript groovy xmlhttprequest apache-poi micronaut


【解决方案1】:

您的 MS Excel (XLSX) 文件在服务器上生成并通过您的 API 发送回客户端。您可以使用responseType = blob 将响应下载为文件。您还应该在浏览器上设置文件名,以便为下载的文件命名。

试试下面的 sn-p。

const exportData = (filteredRows,activity) => {
    let filename = "TestReport.xlsx";
    let xmlHttpRequest = new XMLHttpRequest();
    xmlHttpRequest.onreadystatechange = function() {
        var a;
        if (xmlHttpRequest.readyState === 4 && xmlHttpRequest.status === 200) {
            a = document.createElement('a');
            a.href = window.URL.createObjectURL(xmlHttpRequest.response);
            a.download = filename;
            a.style.display = 'none';
            document.body.appendChild(a);
            a.click();
        }
    };
    xmlHttpRequest.open("POST", '/abc/api/v1/export/'+ activity);
    xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
    xmlHttpRequest.responseType = 'blob';
    xmlHttpRequest.send(JSON.stringify({
        "key": "8575",
        "type":"userdetails",
        "data":filteredRows
    }));
}

【讨论】:

  • 我用上面的脚本试过了,火狐有问题,它是用 .json 文件下载的,而不是 xlsx。
  • 它仍在为我在所有浏览器中下载损坏的文件
【解决方案2】:

从节点发送excel文件,然后我这样做了-

axios({
  url: 'http://api.dev/file-download',
  method: 'GET',
  responseType: 'blob', // important
}).then((response) => {
   const url = window.URL.createObjectURL(new Blob([response.data]));
   const link = document.createElement('a');
   link.href = url;
   link.setAttribute('download', 'file.pdf'); //or any other extension
   document.body.appendChild(link);
   link.click();
});

【讨论】:

  • 顺便点击后好用document.querySelector('body').removeChild(a);
【解决方案3】:

感谢@AabinGunz。我可以放 cmets,因为我没有 50 声望。但他的 sn-p 有效。 我有来自之前设置的变量的 jsonString。下载的 xlsx 文件没有损坏。 再次感谢 AabinGunz

    const exportData = () => {
    let filename = userName+".xlsx";
    let xmlHttpRequest = new XMLHttpRequest();
    xmlHttpRequest.onreadystatechange = function() {
        var a;
        if (xmlHttpRequest.readyState === 4 && xmlHttpRequest.status === 200) {
            a = document.createElement('a');
            a.href = window.URL.createObjectURL(xmlHttpRequest.response);
            a.download = filename;
            a.style.display = 'none';
            document.body.appendChild(a);
            a.click();
        }
    };
    xmlHttpRequest.open("POST", '/calc-reverse-matches');
    xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
    xmlHttpRequest.responseType = 'blob';
    xmlHttpRequest.send(JSON.stringify(jsonString));
}

【讨论】:

    【解决方案4】:

    我在我的项目中也遇到过同样的问题,但我很喜欢在您的请求标题中添加 responseType: "arraybuffer"}

    响应是一个包含二进制数据的 JavaScript ArrayBuffer。

    【讨论】:

    • 我在 axios 标头中尝试了headers: { 'Content-type': 'application/json', 'responseType': 'arraybuffer' },但在打开下载的 excel 文件时遇到了同样的错误。
    猜你喜欢
    • 1970-01-01
    • 2018-04-18
    • 2021-08-19
    • 2021-02-04
    • 1970-01-01
    • 2015-03-17
    • 2015-06-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多