Search code examples
javaexcelperformanceapache-poi

XLSX service to write recursive from String array Java POI


I'm trying to implement a recursive function to add each subsequent array to xlsx file path. I have a ResultSet converted to String arrays that needs to be put into this file. I've got:

java.lang.RuntimeException: java.io.IOException: Stream closed

I found this error comes from workbook.write()
How to separate that in recursive?

import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class XlsxService {

    private final Path outputFilePath;
    private SXSSFWorkbook workbook;
    private SXSSFSheet sheet;
    private Cell cell;
    private int rowNum = 0;

    public XlsxService(Path outputFilePath) {
        this.outputFilePath = outputFilePath;
    }

    public void generate(String[] resultSet) {
        if(workbook == null) {
            workbook = new SXSSFWorkbook();
            sheet = workbook.createSheet("Sheet");
        }
        writeToFile(resultSet);
        try (OutputStream fos = Files.newOutputStream(outputFilePath)) {
            workbook.write(fos);
            workbook.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void writeToFile(String[] params) {       
        Row dataRow = sheet.createRow(rowNum);
        for (int i = 0; i < params.length; i++) {
            dataRow.createCell(i).setCellValue(params[i]);         
        }
        rowNum++;
    }
}

ResultSet examples:

    String[] resultSet1 = ["A1", "B1", "C1"]
    String[] resultSet2 = ["A2", "B2", "C2"]
    ...
    String[] resultSetN = ["AN", "BN", "CN"]

Updated code:

    public void generate(String[] resultSet) {
        addRow(resultSet);
        save();
    }

    public void save() throws IOException {
        try (OutputStream fos = Files.newOutputStream(outputFilePath)) {
            workbook.write(fos);
        }
        workbook.close();
    }

    public void addRow(String[] params) {       
        Row dataRow = sheet.createRow(rowNum);
        for (int i = 0; i < params.length; i++) {
            dataRow.createCell(i).setCellValue(params[i]);         
        }
        rowNum++;
    }


Solution

  • You are writing same workbook each time, it get closed after first write - hence exception on second attempt.

    As you intend to write multiple rows a better structure would replace writeToFile by addRow, and replace generate by save:

    // Initialise once in constructor:
    private final SXSSFWorkbook workbook = new SXSSFWorkbook();
    private final SXSSFSheet sheet = workbook.createSheet("Sheet");
    ...
    // save replaces generate(String[])
    public void save() throws IOException {
        try (OutputStream fos = Files.newOutputStream(outputFilePath)) {
            workbook.write(fos);
        }
        workbook.close();
    }
    // addRow replaces writeToFile 
    public void addRow(String[] params) {
        Row dataRow = sheet.createRow(rowNum);
        for (int i = 0; i < params.length; i++) {
            dataRow.createCell(i).setCellValue(params[i]);
        }
        rowNum++;
    }
    

    Then your calls are simpler :- use addRow as many times as needed, then call save once.

    XlsxService app = new XlsxService(Path.of("some.xlsx"));
    
    // Multiple rows to add
    app.addRow(new String[] {"A1", "B1", "C1"});
    app.addRow(new String[] {"A2", "B2", "C2"});
    app.addRow(new String[] {"A3", "B3", "C3"});
    
    // One save:
    app.save();
    

    if you are looking for all in one call, set Path in the constructor and pass a collection of rows for whatever type is appropriate:

    public void generate(String[][] rows) {
        for (var row : rows)
            addRow(row);
        save();
    }
    // or
    public void generate(List<String[]> rows) {
        for (var row : rows)
            addRow(row);
        save();
    }