最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

groovy - How to download XLSX file from a server response in javascript? - Stack Overflow

programmeradmin2浏览0评论

I have a react frontend and java server using the Micronaut framework. In the BaseController of my Micronaut Framework, I am creating an XLSX file and then sending the response so that it gets downloaded when called from the frontend code.

BaseController

@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))
    }
}

ExportService

Note: Using the main method of this class I am able to create a TestReport.xlsx file. XLSXExporter class uses Apache POI library to generate XLSX file.

@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
        **/
    }
}

I tested the above API from Postman and it is able to download an XLSX file.

When I call the above export API using my application (javascript), it is able to download an xlsx file but it does not open.

Javascript code to call export API

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);
           }
    });
}

Once I click yes on the above dialog I get below error and nothing gets displayed as opposed to the file downloaded from Postman.

I have a react frontend and java server using the Micronaut framework. In the BaseController of my Micronaut Framework, I am creating an XLSX file and then sending the response so that it gets downloaded when called from the frontend code.

BaseController

@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))
    }
}

ExportService

Note: Using the main method of this class I am able to create a TestReport.xlsx file. XLSXExporter class uses Apache POI library to generate XLSX file.

@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
        **/
    }
}

I tested the above API from Postman and it is able to download an XLSX file.

When I call the above export API using my application (javascript), it is able to download an xlsx file but it does not open.

Javascript code to call export API

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);
           }
    });
}

Once I click yes on the above dialog I get below error and nothing gets displayed as opposed to the file downloaded from Postman.

Share Improve this question edited Mar 23, 2020 at 8:52 Ricky asked Mar 23, 2020 at 8:44 RickyRicky 3242 gold badges7 silver badges23 bronze badges 1
  • 1 Not directly a solution to this issue but a different way of achieving the same result: instead of an axios call and generating an <a>, try generating a <form method="post"> with inputs holding the data and submitting it. Since the server sends back a file, submitting the form by calling submit() on it should also trigger a download. – user5734311 Commented Mar 23, 2020 at 8:52
Add a comment  | 

4 Answers 4

Reset to default 10

Your MS Excel (XLSX) file generated on the server and sent back to the client via your API. You can download the response as a file by using responseType = blob. You should also set a filename on your browser to give a name to the downloaded file.

Try below snippet.

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
    }));
}

From node is send the excel file , then i did this -

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();
});

I have also faced the same issue in my project but reloved by adding responseType: "arraybuffer"} in the header of your request.

The response is a JavaScript ArrayBuffer containing binary data.

Thanks to @AabinGunz. I could put comments since I don't have 50 reputation. but his snippet works. I have the jsonString coming from a variable set before. the xlsx file downloaded is not corrupted. thanks again to 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));
}
发布评论

评论列表(0)

  1. 暂无评论