处理大型xlsx文件

时间:2011-02-04 12:05:51

标签: java apache-poi out-of-memory xlsx xssf

我需要自动调整大(30k +行)xlsx文件中的所有行。

以下代码通过apache poi适用于小文件,但在大文件上使用OutOfMemoryError

Workbook workbook = WorkbookFactory.create(inputStream);
Sheet sheet = workbook.getSheetAt(0);

for (Row row : sheet) {
    row.setHeight((short) -1);
}

workbook.write(outputStream);

更新:不幸的是,增加堆大小不是一个选项 - OutOfMemoryError出现在-Xmx1024m,而且30k行不是上限。

10 个答案:

答案 0 :(得分:33)

尝试使用事件API。有关详细信息,请参阅POI文档中的Event API (HSSF only)XSSF and SAX (Event API)。该页面的几个引用:

HSSF:

  

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

XSSF:

  

如果内存占用是个问题,那么对于XSSF,您可以获取基础XML数据并自行处理。这适用于愿意学习.xlsx文件的一些低级结构的中间开发人员,以及在java中处理XML的人。它使用起来比较简单,但需要对文件结构有基本的了解。提供的优势是您可以读取内存占用相对较小的XLSX文件。

对于输出,博客文章Streaming xlsx files中描述了一种可能的方法。 (基本上,使用XSSF生成容器XML文件,然后将实际内容作为纯文本流式传输到xlsx zip存档的相应xml部分。)

答案 1 :(得分:10)

使用File而不是Stream可以显着改善内存使用率。 (最好使用流API,但Streaming API有局限性,请参阅http://poi.apache.org/spreadsheet/index.html

所以而不是

Workbook workbook = WorkbookFactory.create(inputStream);

DO

Workbook workbook = WorkbookFactory.create(new File("yourfile.xlsx"));

这是根据:http://poi.apache.org/spreadsheet/quick-guide.html#FileInputStream

文件与InputStreams

“打开工作簿时,无论是.xls HSSFWorkbook还是.xlsx XSSFWorkbook,都可以从File或InputStream加载工作簿。使用File对象可以降低内存消耗,而InputStream需要更多内存因为它必须缓冲整个文件。“

答案 2 :(得分:3)

我遇到的问题是行数少了很多,但字符串很大。

由于我不必保持数据加载,我发现我可以使用SXSSF而不是XSSF。

它们具有类似的接口,如果您已经编写了大量代码,这将有所帮助。但是使用SXSSF,可以设置保持加载的行数。

这是链接。 http://poi.apache.org/spreadsheet/how-to.html#sxssf

答案 3 :(得分:2)

如果您想自动调整或设置样式或在大(30k +行)xlsx文件中写入所有行,请使用SXSSFWorkbook。这是帮助您的示例代码...

SXSSFWorkbook wb = new SXSSFWorkbook();
            SXSSFSheet sheet = (SXSSFSheet) wb.createSheet("writetoexcel");
            Font font = wb.createFont();
                font.setBoldweight((short) 700);
                // Create Styles for sheet.
                XSSFCellStyle Style = (XSSFCellStyle) wb.createCellStyle();
                Style.setFillForegroundColor(new XSSFColor(java.awt.Color.LIGHT_GRAY));
                Style.setFillPattern(XSSFCellStyle.SOLID_FOREGROUND);
                Style.setFont(font);
                //iterating r number of rows
            for (int r=0;r < 30000; r++ )
            {
                Row row = sheet.createRow(r);
                //iterating c number of columns
                for (int c=0;c < 75; c++ )
                {
                    Cell cell = row.createCell(c);
                    cell.setCellValue("Hello"); 
                    cell.setCellStyle(Style);
                }
    }
            FileOutputStream fileOut = new FileOutputStream("E:" + File.separator + "NewTest.xlsx");

答案 4 :(得分:1)

我将事件API用于HSSF文件(.xls),我发现可怕的记录顺序缺乏文档。

答案 5 :(得分:0)

这是我发现的一个例子,它将处理非常大的XLSX文件。到目前为止我的测试看起来不错它能够处理非常大的文件而不会出现内存问题。

http://svn.apache.org/repos/asf/poi/trunk/src/examples/src/org/apache/poi/xssf/eventusermodel/XLSX2CSV.java

答案 6 :(得分:0)

如果您正在写到XLSX,我通过写入同一Excel文件的不同表单找到了改进。您也可以通过写入不同的Excel文件来找到改进。但首先尝试写不同的表格。

答案 7 :(得分:0)

以下堆栈溢出线程中描述了最佳示例: Error While Reading Large Excel Files (xlsx) Via Apache POI

该主题主要答案中的代码片段说明了围绕SAX xml解析的Apache POI包装,以及如何轻松地遍历所有工作表,然后遍历每个单独的单元格。

代码在Apache POI API的当前实现中是陈旧的,因为endRow()api提供了已经完成处理的当前行号。

使用该代码片段,您可以轻松地逐个解析大型XLSX文件。例如。每张纸;对于每个行单元格;行结束了事件。 您可以创建应用程序逻辑,在每行创建一个columneName到cellValue的Map。

答案 8 :(得分:0)

我遇到了800,000个单元格和3M字符的问题,其中XSSF分配了1GB的堆!

我使用Python openpyxlnumpy来读取xlsx文件(来自Java代码)并首先将其转换为普通文本。然后我在java中加载了文本文件。它似乎有很大的开销,但确实很快。

python脚本看起来像

import openpyxl as px
import numpy as np

# xlsx file is given through command line foo.xlsx
fname = sys.argv[1]
W = px.load_workbook(fname, read_only = True)
p = W.get_sheet_by_name(name = 'Sheet1')

a=[]
# number of rows and columns
m = p.max_row
n = p.max_column

for row in p.iter_rows():
    for k in row:
        a.append(k.value)

# convert list a to matrix (for example maxRows*maxColumns)
aa= np.resize(a, [m, n])

# output file is also given in the command line foo.txt
oname = sys.argv[2]
print (oname)
file = open(oname,"w")
mm = m-1
for i in range(mm):
    for j in range(n):
        file.write( "%s " %aa[i,j]  )
    file.write ("\n")

# to prevent extra newline in the text file
for j in range(n):
    file.write("%s " %aa[m-1,j])

file.close()

然后在我的java代码中,我写了

try {
  // `pwd`\python_script  foo.xlsx  foo.txt
  String pythonScript =  System.getProperty("user.dir") + "\\exread.py ";
  String cmdline = "python " + pythonScript +
                    workingDirectoryPath + "\\" + fullFileName + " " + 
                    workingDirectoryPath + "\\" + shortFileName + ".txt";
  Process p = Runtime.getRuntime().exec(cmdline);
  int exitCode = p.waitFor();
  if (exitCode != 0) {
    throw new IOException("Python command exited with " + exitCode);
  }
} catch (IOException e) {
  System.out.println( e.getMessage() );
} catch (InterruptedException e) {
  ReadInfo.append(e.getMessage() );
}

之后,您将获得类似于foo.xlsx的foo.txt,但是采用文本格式。

答案 9 :(得分:0)

我使用SAX解析器来处理XML结构。它适用于XLSX文件。

https://stackoverflow.com/a/44969009/4587961