Problem generating an excel file download in a Portlet

Carlos Barreiro, modified 5 Years ago. New Member Posts: 16 Join Date: 11/25/19 Recent Posts
Hello, i'm generating an excel with POI library, and if I save the workbook as a file it is being successfully saved, and I can open it without problems.
But if I want to send the file as a download in a Portlet with the ServletResponseUtil.sendFile function, the file that I get seems to be corrupted because Excel can't open it. And in fact the file is 2KB bigger than the good file.
Do you know what could I be doing wrong? I've been struggling with this for a good amount of time and I can't see where the problem is.
//Create the workbook
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("Example");
Object[][] datalist = {
    {"First Header", "Second Header"},
    {"test1", "test2"},
    {"test3", "test4"}
};

​​​​​​​int rowNum = 0;
for (Object[] data : datalist) {
    Row row = sheet.createRow(rowNum++);
    int colNum = 0;
    for (Object field : data) {
        Cell cell = row.createCell(colNum++);
        if (field instanceof String) {
            cell.setCellValue((String) field);
        } else if (field instanceof Integer) {
            cell.setCellValue((Integer) field);
        }
    }
}

// Saving the workbook in an excel file. It works OK
FileOutputStream outputStream = new FileOutputStream("/tmp/excelTest.xlsx");
workbook.write(outputStream);

// Returning the file via servlet response. It returns a corrupted file.
HttpServletRequest servletRequest = PortalUtil.getHttpServletRequest(renderRequest);
HttpServletResponse servletResponse = PortalUtil.getHttpServletResponse(renderResponse);
FileInputStream excelFile = new FileInputStream("/tmp/excelTest.xlsx");
ServletResponseUtil.sendFile(servletRequest, servletResponse, "excelTest.xlsx", excelFile);

​​​​​​​workbook.close();
thumbnail
Fernando Fernandez, modified 5 Years ago. Expert Posts: 401 Join Date: 8/22/07 Recent Posts
Hi Carlos,
I'm assuming if you try and open /tmp/excelTest.xlsx it's working fine. 
Normally I would solve this problem with a link to a serveResource and I never used this method. But it strikes me as odd that you close the file only after sending it. 

I would also investigate the mimetype that the file is being sent with.
HTH
Fernando
thumbnail
Olaf Kock, modified 5 Years ago. Liferay Legend Posts: 6441 Join Date: 9/23/08 Recent Posts
Just as Fernando says: This must happen in a serveResource method. Accessing the HttpServletResponse from a render method probably embeds your file within the portal's HTML page, which explains the additional 2K and the invalid content. You're working around the provided API and shouldn't do that. There's also no need to go through a temporary file, but that's a different topic.
A render method must always return fragments of HTML for the portal page. You're willingly ignoring this - and you can observe the result
Carlos Barreiro, modified 5 Years ago. New Member Posts: 16 Join Date: 11/25/19 Recent Posts
Thank you so much Fernando and Olaf, I knew i was taking a wrong approach ^^U

As you said, using the serveResource is the correct approach and, in fact, it returns a correct file.

public void serveResource(ResourceRequest resourceRequest, ResourceResponse resourceResponse) throws IOException {
    //Create the workbook
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("Example");
    Object[][] datalist = {
            {"First Header", "Second Header"},
            {"test1", "test2"},
            {"test3", "test4"}
    };

​​​​​​​    int rowNum = 0;
    for (Object[] data : datalist) {
        Row row = sheet.createRow(rowNum++);
        int colNum = 0;
        for (Object field : data) {
            Cell cell = row.createCell(colNum++);
            if (field instanceof String) {
                cell.setCellValue((String) field);
            } else if (field instanceof Integer) {
                cell.setCellValue((Integer) field);
            }
        }
    }
    
    resourceResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    workbook.write(resourceResponse.getPortletOutputStream());

    workbook.close();
}