使用 POI 将大型结果集写入 Excel 文件

2022-09-01 02:00:35

这是一种内联,将一个大的结果集写入文件,但有问题的文件是一个Excel文件。

我正在使用Apache POI库编写一个Excel文件,其中包含从ResultSet对象检索到的大型数据集。数据范围可以从几千条记录到大约100万条记录;不确定这如何转换为Excel格式的文件系统字节。

以下是我编写的测试代码,用于检查编写如此大的结果集所花费的时间,以及 w.r.t CPU & Memory 的性能影响。

protected void writeResultsetToExcelFile(ResultSet rs, int numSheets, String fileNameAndPath) throws Exception {

    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileNameAndPath));
    int numColumns = rs.getMetaData().getColumnCount();

    Workbook wb = ExcelFileUtil.createExcelWorkBook(true, numSheets);
    Row heading = wb.getSheetAt(0).createRow(1);

    ResultSetMetaData rsmd = rs.getMetaData();

    for(int x = 0; x < numColumns; x++) {
        Cell cell = heading.createCell(x+1);
        cell.setCellValue(rsmd.getColumnLabel(x+1));
    }

    int rowNumber = 2;
    int sheetNumber = 0;

    while(rs.next()) {

        if(rowNumber == 65001) {
            log("Sheet " + sheetNumber + "written; moving onto to sheet " + (sheetNumber + 1));
            sheetNumber++;
            rowNumber = 2;
        }

        Row row = wb.getSheetAt(sheetNumber).createRow(rowNumber);
        for(int y = 0; y < numColumns; y++) {
            row.createCell(y+1).setCellValue(rs.getString(y+1));
            wb.write(bos);
        }

        rowNumber++;
    }

    //wb.write(bos);

    bos.close();
}

上面的代码没有太多的运气。创建的文件似乎增长迅速(每秒约70Mb)。因此,我在大约10分钟后停止了执行(当文件达到7Gb时终止了JVM),并尝试在Excel 2007中打开该文件。当我打开它的那一刻,文件大小变为8k(!),只创建标题和第一行。不知道我在这里错过了什么。

有什么想法吗?


答案 1

使用 SXSSF poi 3.8

package example;

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class SXSSFexample {


    public static void main(String[] args) throws Throwable {
        FileInputStream inputStream = new FileInputStream("mytemplate.xlsx");
        XSSFWorkbook wb_template = new XSSFWorkbook(inputStream);
        inputStream.close();

        SXSSFWorkbook wb = new SXSSFWorkbook(wb_template); 
        wb.setCompressTempFiles(true);

        SXSSFSheet sh = (SXSSFSheet) wb.getSheetAt(0);
        sh.setRandomAccessWindowSize(100);// keep 100 rows in memory, exceeding rows will be flushed to disk
    for(int rownum = 4; rownum < 100000; rownum++){
        Row row = sh.createRow(rownum);
        for(int cellnum = 0; cellnum < 10; cellnum++){
            Cell cell = row.createCell(cellnum);
            String address = new CellReference(cell).formatAsString();
            cell.setCellValue(address);
        }

    }


    FileOutputStream out = new FileOutputStream("tempsxssf.xlsx");
    wb.write(out);
    out.close();
}

}

它需要:

  • poi-ooxml-3.8.jar,
  • poi-3.8.jar,
  • poi-ooxml-schemas-3.8.jar,
  • stax-api-1.0.1.jar,
  • xml-apis-1.0.b2.jar,
  • xmlbeans-2.3.0.jar,
  • commons-codec-1.5.jar,
  • dom4j-1.6.1.jar

有用的链接


答案 2

哦。我认为您正在将工作簿写出944,000次。您的 wb.write(bos) 调用位于内部循环中。我不确定这是否与工作簿类的语义完全一致?根据我在该类的Javadocs中可以看出,该方法将整个工作簿写出到指定的输出流中。随着事物的增长,它将写出到目前为止您添加的每一行,每一行一次。

这就解释了为什么您也看到 1 行。要写出到文件中的第一个工作簿(带有一行)就是显示的所有内容 - 然后是7GB的垃圾。


推荐