8

我正在尝试使用 Java 验证 Excel 文件,然后再将其转储到数据库。

这是我的导致错误的代码片段。

try {
    fis = new FileInputStream(file);
    wb = new XSSFWorkbook(fis);
    XSSFSheet sh = wb.getSheet("Sheet1");
    for(int i = 0 ; i < 44 ; i++){
        XSSFCell a1 = sh.getRow(1).getCell(i);
        printXSSFCellType(a1);
    }
    
} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

这是我得到的错误

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.ArrayList.<init>(Unknown Source)
    at java.util.ArrayList.<init>(Unknown Source)
    at org.apache.xmlbeans.impl.values.NamespaceContext$NamespaceContextStack.<init>(NamespaceContext.java:78)
    at org.apache.xmlbeans.impl.values.NamespaceContext$NamespaceContextStack.<init>(NamespaceContext.java:75)
    at org.apache.xmlbeans.impl.values.NamespaceContext.getNamespaceContextStack(NamespaceContext.java:98)
    at org.apache.xmlbeans.impl.values.NamespaceContext.push(NamespaceContext.java:106)
    at org.apache.xmlbeans.impl.values.XmlObjectBase.check_dated(XmlObjectBase.java:1273)
    at org.apache.xmlbeans.impl.values.XmlObjectBase.stringValue(XmlObjectBase.java:1484)
    at org.apache.xmlbeans.impl.values.XmlObjectBase.getStringValue(XmlObjectBase.java:1492)
    at org.openxmlformats.schemas.spreadsheetml.x2006.main.impl.CTCellImpl.getR(Unknown Source)
    at org.apache.poi.xssf.usermodel.XSSFCell.<init>(XSSFCell.java:105)
    at org.apache.poi.xssf.usermodel.XSSFRow.<init>(XSSFRow.java:70)
    at org.apache.poi.xssf.usermodel.XSSFSheet.initRows(XSSFSheet.java:179)
    at org.apache.poi.xssf.usermodel.XSSFSheet.read(XSSFSheet.java:143)
    at org.apache.poi.xssf.usermodel.XSSFSheet.onDocumentRead(XSSFSheet.java:130)
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.onDocumentRead(XSSFWorkbook.java:286)
    at org.apache.poi.POIXMLDocument.load(POIXMLDocument.java:159)
    at org.apache.poi.xssf.usermodel.XSSFWorkbook.<init>(XSSFWorkbook.java:207)
    at com.xls.validate.ExcelValidator.main(ExcelValidator.java:79)

当 .xlsx 文件小于 1 MB 时,这可以正常工作。

我知道这是因为我的 .xlsx 文件大约 5-10 MB,并且 POI 尝试将整个工作表一次加载到 JVM 内存中。

什么是可能的解决方法?

4

7 回答 7

10

有两个选项可供您使用。选项 #1 - 增加 JVM 堆的大小,以便 Java 有更多可用内存。使用 UserModel 代码在 POI 中处理 Excel 文件是基于 DOM 的,因此需要将整个文件(包括解析后的表单)缓冲到内存中。尝试这样一个问题,以获取有关如何增加帮助的建议。

选项 #2,工作量更大 - 切换到基于事件 (SAX) 的处理。这一次只处理文件的一部分,因此需要的内存要少得多。然而,它需要你做更多的工作,这就是为什么你最好在问题上投入更多 GB 的内存 - 内存很便宜,而程序员却没有!SpreadSheet howto 页面有关于如何对 .xlsx 文件进行 SAX 解析的说明,您可以查看POI 提供的各种示例文件以获取建议。

.

另外,另一件事-您似乎正在通过流加载文件,这很糟糕,因为这意味着更多的东西需要缓冲到内存中。有关更多信息,请参阅POI 文档,包括有关如何直接使用文件的说明。

于 2013-08-09T15:04:09.907 回答
2

您可以使用 POI 中的 SXSSF 工作簿来解决与内存相关的问题。参考这里

在读取多个 CSV 并将其合并到单个 XLSX 文件中时,我遇到了类似的问题。我总共有 3 个 csv 表,每个表有 30k 行,总计 90k。

它通过使用 SXSFF 得到解决,如下所示,

    public static void mergeCSVsToXLSX(Long jobExecutionId, Map<String, String> csvSheetNameAndFile, String xlsxFile) {
    try (SXSSFWorkbook wb = new SXSSFWorkbook(100);) { // keep 100 rows in memory, exceeding rows will be flushed to
                                                       // disk
      csvSheetNameAndFile.forEach((sheetName, csv) -> {
        try (CSVReader reader = new CSVReader(new FileReader(csv))) {
          wb.setCompressTempFiles(true);
          SXSSFSheet sheet = wb.createSheet(sheetName);
          sheet.setRandomAccessWindowSize(100);

          String[] nextLine;
          int r = 0;
          while ((nextLine = reader.readNext()) != null) {
            Row row = sheet.createRow((short) r++);
            for (int i = 0; i < nextLine.length; i++) {
              Cell cell = row.createCell(i);
              cell.setCellValue(nextLine[i]);
            }
          }
        } catch (IOException ioException) {
          logger.error("Error in reading CSV file {} for jobId {} with exception {}", csv, jobExecutionId,
              ioException.getMessage());
        }
      });

      FileOutputStream out = new FileOutputStream(xlsxFile);
      wb.write(out);
      wb.dispose();
    } catch (IOException ioException) {
      logger.error("Error in creating workbook for jobId {} with exception {}", jobExecutionId,
          ioException.getMessage());
    }
  }
于 2019-03-09T21:56:24.510 回答
1

使用Event API (HSSF Only).

事件 API 比用户 API 更新。它适用于愿意学习一点低级 API 结构的中级开发人员。它使用起来相对简单,但需要对 Excel 文件的各个部分有基本的了解(或愿意学习)。提供的优点是您可以读取内存占用相对较小的 XLS 。

于 2013-08-09T13:58:17.630 回答
0

好吧,这里有一个链接,其中包含有关您的错误的一些详细信息以及如何解决它:http: //javarevisited.blogspot.com/2011/09/javalangoutofmemoryerror-permgen-space.html ?m=1 。

好吧,让我试着解释你的错误:

java.lang.OutOfMemoryError两种变体。一个在 Java 堆空间,另一个在 PermGen 空间。

您的错误可能是由内存泄漏、系统 RAM 量低或分配给 Java 虚拟机的 RAM 太少引起的。

Java Heap Space 和 PermGen Space 变体之间的区别在于 PermGen Space 存储字符串池和原始类型(例如 int)上的数据,以及如何读取方法和类,Java Heap Space 的工作方式不同。因此,如果您的项目中有很多字符串或类,并且没有足够的分配/系统 RAM,您将收到 OutOfMemoryError。JVM 分配给 PermGen 的默认 RAM 量是 64 MB,这是相当小的内存空间。链接的文章解释了有关此错误的更多信息,并提供了有关如何解决此问题的详细信息。

希望这可以帮助!

于 2013-08-09T13:34:10.520 回答
0

要解决Outofmemery错误,请按照此操作。

您不能修改 a 中的现有单元格SXSSFWorkbook,但可以使用SXSSFWorkbook.

通过将工作簿对象与rowaccesswindow大小一起传递是可能的。

SXSSFWorkbook workbook = new SXSSFWorkbook(  new XSSFWorkbook(new FileInputStream(file)),100);
//Your changes in workbook
workbook.write(out);
于 2021-10-10T06:32:20.593 回答
0

要解决 Outofmemery 错误,请按照此操作。

您不能修改 SXSSFWorkbook 中的现有单元格,但您可以使用 SXSSFWorkbook 与您的修改一起创建新文件。

通过将工作簿对象与 rowaccesswindow 大小一起传递是可能的。

SXSSFWorkbook workbook = new SXSSFWorkbook(  new XSSFWorkbook(new FileInputStream(file)),100);

//Your changes in workbook

workbook.write(out);
于 2021-10-10T06:43:35.880 回答
-1

我在解析xlsx文件时也遇到了同样的OOM问题……经过两天的努力,我终于发现下面的代码非常完美;

此代码基于 sjxlsx。它读取 xlsx 并存储在 HSSF 表中。

           [code=java] 
            // read the xlsx file
       SimpleXLSXWorkbook = new SimpleXLSXWorkbook(new File("C:/test.xlsx"));

        HSSFWorkbook hsfWorkbook = new HSSFWorkbook();

        org.apache.poi.ss.usermodel.Sheet hsfSheet = hsfWorkbook.createSheet();

        Sheet sheetToRead = workbook.getSheet(0, false);

        SheetRowReader reader = sheetToRead.newReader();
        Cell[] row;
        int rowPos = 0;
        while ((row = reader.readRow()) != null) {
            org.apache.poi.ss.usermodel.Row hfsRow = hsfSheet.createRow(rowPos);
            int cellPos = 0;
            for (Cell cell : row) {
                if(cell != null){
                    org.apache.poi.ss.usermodel.Cell hfsCell = hfsRow.createCell(cellPos);
                    hfsCell.setCellType(org.apache.poi.ss.usermodel.Cell.CELL_TYPE_STRING);
                    hfsCell.setCellValue(cell.getValue());
                }
                cellPos++;
            }
            rowPos++;
        }
        return hsfSheet;[/code]
于 2013-10-20T07:26:14.277 回答