I have a problem sending an Excel file in blocks. I have this code that is supposed to do this, and it works fine up to a certain point. However, the issue is that it only writes the first 100 records. After that, the file size remains the same as if it had all 10,000 records, but when opened, it only has 100 records.
@Override
public void generateAndSendExcelChunks(ServletOutputStream outputStream) throws IOException {
Workbook workbook = new SXSSFWorkbook();
Sheet sheet = workbook.createSheet("Datos");
int totalRows = 10000;
int rowsPerChunk = 100;
int currentRow = 0;
while (currentRow < totalRows) {
int endRow = currentRow + rowsPerChunk;
endRow = Math.min(endRow, totalRows);
generateChunkData(sheet, currentRow, endRow);
workbook.write(outputStream);
outputStream.flush();
sheet = workbook.createSheet("Datos");
currentRow = endRow;
}
workbook.close();
}
private void generateChunkData(Sheet sheet, int startRow, int endRow) {
for (int i = startRow; i < endRow; i++) {
Row row = sheet.createRow(i);
Cell cell = row.createCell(0);
cell.setCellValue("Valor " + (i + 1));
}
}
The line sheet = workbook.createSheet("Datos"); doesn't work because it tries to create a sheet with a name that already exists. If I try to get a reference to that sheet, it gives a "stream closed" error on this line: workbook.write(outputStream);. The intended functionality is to store all the information in the same sheet without overwriting anything.
What you are trying to do is not possible. At least not using Apache POI up to now.
Office Open XML files, such as
*.xlsxfiles too, are ZIP archives containing the data in a special internally file structure. The ZIP archive contains*.xmlfiles and other files too in a special directory structure. The different parts are related to each other. The relations are stored in*.relfiles storing special relation-XML. One can see this if one simply unzips the*.xlsxfile for example. 7-Zip can do this by default, using other ZIP-Software one needs renaming the file*.zip.After knowing this and have seen this structure, think about how to fill this chunk by chunk considering all the relations. Conclusion: Very hard to nearly impossible.
Opening a
Workbookobject of any kind opens that ZIP archive and reads internally stored files into memory. AndWorkbook.writewrites the complete internally ZIP content into the file. So afterWorkbook.writethe workbook object is not more usable until it gets opened again. Thus using it in a loop like: put first content in, write, put second content in, write, ... is not possible. It would must be like: open workbook, put first content in, write, close workbook, open workbook, put second content in, write, close workbook, ... But that contradicts your needs as opening the workbook always reads all content into memory.But why not simply using
SXSSFWorkbookas thought by the programmers? See SXSSF (Streaming Usermodel API). Using the default properties this does exactly what you are trying to do. It only remains 100 rows of sheet data in memory. Other sheet data gets written in a temporary sheet XML file. Only other data and relations remaining in memory permanently. When doneSXSSFWorkbook.writecomposes the ZIP archive using the temporary stored sheet data.Of course
SXSSFWorkbook.writealso needs to write the whole*.xlsx-ZIP-archive then. So, if your problem is about delivering a big file to a client, then store the*.xlsxfile server-side usingSXSSFWorkbook.write. Then think about it as a big binary file to deliver (provide to download) to the client. There should be default approaches to solve that kind of task as that is a default kind of task. Self programming the division into chunks is not necessary and is a bad idea in my opinion.