0

I am attempting to generate an .XLSX file and download it. I am using Apache POI to generate the excel file and I am using Swagger UI to test the endpoint. I have been searching for a few hours with little luck. The code I am using to generate my Excel file is:

@GetMapping(path = "/download")
public ResponseEntity<ByteArrayResource> download(@RequestParam(name = "ids") long [] ids) {
    // ... other code ...
    XSSFWorkbook workbook = createWorkbook(reports);

    LocalDate date = LocalDate.now();

    String filename = "MYC " + date.getYear() + "." + date.getMonthValue();

    try {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        HttpHeaders header = new HttpHeaders();
        header.setContentType(new MediaType("application", "vnd.openxmlformats-officedocument.spreadsheetml.sheet"));
        header.set(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s.xlsx", filename));
        workbook.write(stream);
        workbook.close();
        return new ResponseEntity<>(new ByteArrayResource(stream.toByteArray()), header, HttpStatus.CREATED);
    } catch (Exception e) {
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

When testing with Swagger UI, I make the call and I get this in return: enter image description here

An answer I found on SO advised using force-download instead of vnd.openxmlformats-officedocument.spreadsheetml.sheet so I went ahead and gave it a try. This time, Swagger UI was actually generating a Download file link. I can click it and the file would download just fine. But is using force-download really an appropriate method?

Now, I attempted to download on my front-end and no matter if I am using force-download or vnd.openxmlformats-officedocument.spreadsheetml.sheet, I just get a 'corrupted' .xlsx file.

Just found this SO question while writing my own, and this person seems to also receive the same type of response. He fixed it by converting his binary data to string. Not sure if that is what I should do as well and how I would go about it.

Alexiz Hernandez
  • 609
  • 2
  • 9
  • 31

1 Answers1

-1

Okay, so I managed to figure it out by gathering information from multiple google searches. For my backend, I encoded byte array to Base64, then returned it as a String.

@GetMapping(path = "/download")
public ResponseEntity<String> download(@RequestParam(name = "ids") long[] ids) {
    // ... other code ...
    XSSFWorkbook workbook = createWorkbook(reports);

    try {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType("text", "plain");
        workbook.write(outputStream);
        workbook.close();
        return new ResponseEntity<>(Base64.getEncoder().encodeToString(outputStream.toByteArray(), headers, HttpStatus.OK);
    } catch (IOException e) {
        return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

On the frontend, I decoded the String using atob(), then to ArrayBuffer

// ... other code ...
actions.getSpreadsheet(ids)
    .then(result => {
        var date = new Date();
        var filename = "JSC " +
            date.getFullYear() +
            "." +
            (date.getMonth() + 1);

        var url = window.URL.createObjectURL(new Blob([this.s2ab(atob(result.data))], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;' }));
        var a = document.createElement('a');
        a.href = url;
        a.download = filename + '.xlsx';
        document.body.appendChild(a);
        a.click();
        a.remove();
    }).catch(error => {
        alert('Error retrieving report: ' + error);
    })

s2ab = s => {
    var buf = new ArrayBuffer(s.length);
    var view = new Unit8Array(buf);
    for (var i = 0; i != s.length; i++) {
        view[i] = s.charCodeAt(i) & 0xFF;
    }
    return buf;
}
Alexiz Hernandez
  • 609
  • 2
  • 9
  • 31