40

我需要自动适应大型(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出现在-Xmx1024m30k 行不是上限。

4

9 回答 9

36

尝试使用事件 API。有关详细信息,请参阅POI 文档中的事件 API(仅限 HSSF)XSSF 和 SAX(事件 API)。该页面的一些报价:

HSSF:

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

XSSF:

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

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

于 2011-02-18T06:58:46.267 回答
11

通过使用文件而不是流可以显着提高内存使用率。(最好使用流式 API,但流式 API 有局限性,请参阅http://poi.apache.org/spreadsheet/index.html

所以而不是

Workbook workbook = WorkbookFactory.create(inputStream);

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

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

文件与输入流

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

于 2013-07-09T07:29:41.057 回答
3

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

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

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

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

于 2013-05-23T04:34:19.710 回答
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");
于 2015-02-28T06:54:21.347 回答
1

我对 HSSF 文件 (.xls) 使用了 Event API,但我发现关于记录顺序的文档非常缺乏。

于 2011-03-17T01:28:24.947 回答
0

如果您正在写入XLSX,我发现通过写入同一 Excel 文件的不同工作表可以得到改进。您还可以通过写入不同的 Excel 文件来发现改进。但首先尝试写入不同的工作表。

于 2013-08-04T18:00:37.793 回答
0

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

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

于 2017-07-07T10:57:07.660 回答
0

我在 XSSF 分配 1GB 堆的 800,000 个单元和 3M 字符时遇到了同样的问题!

我使用 Pythonopenpyxlnumpy读取 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,但为文本格式。

于 2017-05-05T18:44:26.397 回答
0

以下堆栈溢出线程中描述了最好的示例: 通过 Apache POI 读取大型 Excel 文件 (xlsx) 时出错

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

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

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

于 2016-01-18T15:10:08.607 回答