我有一个大.xlsx文件(141 MB,将含有293413行,每行62列)我需要内执行一些操作。
我有装载问题这个文件( OutOfMemoryError
),作为POI对XSSF(XLSX)工作簿大容量内存。
这太问题是相似的,而提出的解决方案是增加虚拟机的分配/最大内存。
这似乎为那种文件大小(9MB)的工作,但对我来说,它只是简单地不即使分配所有可用的系统内存工作。 (好吧,这是毫不奇怪考虑该文件是超过15倍)
我想知道是否有任何方式来加载在某种程度上,它不会消耗所有的内存,然而该工作簿,而不做基于处理(进入)的XSSF的基础XML。 (换句话说,保持清教徒POI溶液)
如果没有坚韧,欢迎您说它(“没有。”),并指向我的方式为“XML”的解决方案。
Answer 1:
我是在一个web服务器环境类似的情况。 在上传的典型大小为150K〜行,它不会一直不错,从单个请求消耗一吨的内存。 在Apache POI流API可以很好地用于这一点,但它需要你读的逻辑的完全重新设计。 我已经使用,我没有想得重做标准API读逻辑的一群,所以我写了这个: https://github.com/monitorjbl/excel-streaming-reader
这不完全是一个下拉更换为标准XSSFWorkbook
类,但如果你只是通过迭代行使其表现:
import com.monitorjbl.xlsx.StreamingReader;
InputStream is = new FileInputStream(new File("/path/to/workbook.xlsx"));
StreamingReader reader = StreamingReader.builder()
.rowCacheSize(100) // number of rows to keep in memory (defaults to 10)
.bufferSize(4096) // buffer size to use when reading InputStream to file (defaults to 1024)
.sheetIndex(0) // index of sheet to use (defaults to 0)
.read(is); // InputStream or File for XLSX file (required)
for (Row r : reader) {
for (Cell c : r) {
System.out.println(c.getStringCellValue());
}
}
还有一些注意事项使用它; 由于方式XLSX片材结构,不是所有的数据是在流的当前窗口中可用。 不过,如果你只是想从细胞中读取简单的数据出来,它工作得很好了点。
Answer 2:
在内存使用量的提高可以通过使用一个文件,而不是流来完成。 (这是更好地使用流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
文件VS InputStreams
“当打开工作簿,无论是一个的.xls HSSFWorkbook或.XLSX XSSFWorkbook,工作簿可以从文件或一个InputStream装载。使用文件对象允许更低的内存消耗,而一个InputStream需要更多的存储器,因为它有缓冲整个文件“。
Answer 3:
在Apache的POI,HSSF和XSSF,该支持Excel支持3种不同的模式。
一个是一个完整的,类似DOM的内存“的usermodel”,它同时支持读取和写入。 使用普通党卫军(电子表格)的接口,你可以为这两个HSSF(.xls的)和XSSF(.xlsx)格式基本上是透明的代码。 但是,它需要大量的内存。
POI还支持流媒体只读方式处理的文件,该EventModel。 这是级别低比的usermodel得多,并让你非常接近的文件格式。 对于HSSF(.xls的),你得到的记录流,以及可选一些帮助处理这些(缺细胞,格式跟踪等)。 对于XSSF格式(.xlsx)你从文件的不同部分SAX事件流,提供帮助,以获取该文件的右侧部分和文件的共同但有小块的也容易处理。
对于XSSF(.XLSX)只,POI还支持一个只写流写入,适用于低的水平,但低存储器的写入。 它在很大程度上只是支持虽然新文件(某些种类的追加是可能的)。 没有HSSF等价的,因背部和往复字节偏移和索引偏移量多条记录这将是非常难以做到...
针对您的特殊情况下,如在澄清意见描述,我想你会想使用XSSF EventModel代码。 见的POI文件上手,然后尝试寻找这 3 类中的POI和提卡它使用它的更多细节。
Answer 4:
POI现在包括对这些案件的API。 SXSSF http://poi.apache.org/spreadsheet/index.html因此它可以让你处理这些文件时,它不会加载在内存中的所有。
注:我已阅读,SXSSF工作作为书写API。 装载应使用不XSSF的inputstream'ing文件来完成(以避免在内存满负荷)
Answer 5:
检查这个职位。 我展示了如何使用SAX解析器来处理XLSX文件。
https://stackoverflow.com/a/44969009/4587961
总之,我扩展org.xml.sax.helpers.DefaultHandler
whih处理XML结构XLSX filez。 t是事件解析 - SAX。
class SheetHandler extends DefaultHandler {
private static final String ROW_EVENT = "row";
private static final String CELL_EVENT = "c";
private SharedStringsTable sst;
private String lastContents;
private boolean nextIsString;
private List<String> cellCache = new LinkedList<>();
private List<String[]> rowCache = new LinkedList<>();
private SheetHandler(SharedStringsTable sst) {
this.sst = sst;
}
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
// c => cell
if (CELL_EVENT.equals(name)) {
String cellType = attributes.getValue("t");
if(cellType != null && cellType.equals("s")) {
nextIsString = true;
} else {
nextIsString = false;
}
} else if (ROW_EVENT.equals(name)) {
if (!cellCache.isEmpty()) {
rowCache.add(cellCache.toArray(new String[cellCache.size()]));
}
cellCache.clear();
}
// Clear contents cache
lastContents = "";
}
public void endElement(String uri, String localName, String name)
throws SAXException {
// Process the last contents as required.
// Do now, as characters() may be called more than once
if(nextIsString) {
int idx = Integer.parseInt(lastContents);
lastContents = new XSSFRichTextString(sst.getEntryAt(idx)).toString();
nextIsString = false;
}
// v => contents of a cell
// Output after we've seen the string contents
if(name.equals("v")) {
cellCache.add(lastContents);
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
lastContents += new String(ch, start, length);
}
public List<String[]> getRowCache() {
return rowCache;
}
}
然后我解析XML预送XLSX文件
private List<String []> processFirstSheet(String filename) throws Exception {
OPCPackage pkg = OPCPackage.open(filename, PackageAccess.READ);
XSSFReader r = new XSSFReader(pkg);
SharedStringsTable sst = r.getSharedStringsTable();
SheetHandler handler = new SheetHandler(sst);
XMLReader parser = fetchSheetParser(handler);
Iterator<InputStream> sheetIterator = r.getSheetsData();
if (!sheetIterator.hasNext()) {
return Collections.emptyList();
}
InputStream sheetInputStream = sheetIterator.next();
BufferedInputStream bisSheet = new BufferedInputStream(sheetInputStream);
InputSource sheetSource = new InputSource(bisSheet);
parser.parse(sheetSource);
List<String []> res = handler.getRowCache();
bisSheet.close();
return res;
}
public XMLReader fetchSheetParser(ContentHandler handler) throws SAXException {
XMLReader parser = new SAXParser();
parser.setContentHandler(handler);
return parser;
}
Answer 6:
您可以使用SXXSF而不是使用HSSF。 我能生成20万行出类拔萃。
Answer 7:
基于monitorjbl的回答和测试套件从POI探讨,以下为我工作的多页文件的xlsx与200K记录(大小> 50 MB):
import com.monitorjbl.xlsx.StreamingReader;
. . .
try (
InputStream is = new FileInputStream(new File("sample.xlsx"));
Workbook workbook = StreamingReader.builder().open(is);
) {
DataFormatter dataFormatter = new DataFormatter();
for (Sheet sheet : workbook) {
System.out.println("Processing sheet: " + sheet.getSheetName());
for (Row row : sheet) {
for (Cell cell : row) {
String value = dataFormatter.formatCellValue(cell);
}
}
}
}
文章来源: How to load a large xlsx file with Apache POI?