Xen*_*oRo 41 java memory out-of-memory apache-poi xssf
我有一个大的.xlsx文件(141 MB,包含293413行,每行62列)我需要在其中执行一些操作.
我在加载此文件(OutOfMemoryError)时遇到问题,因为POI在XSSF(xlsx)工作簿上占用大量内存.
这个问题类似,提出的解决方案是增加VM的分配/最大内存.
它似乎适用于那种文件大小(9MB),但对我来说,即使分配所有可用的系统内存,它也只是不起作用.(嗯,考虑到文件大于15倍,这并不奇怪)
我想知道是否有任何方法以不占用所有内存的方式加载工作簿,但是,没有基于(进入)XSSF的底层XML进行处理.(换句话说,维护清教徒POI解决方案)
如果没有强硬的话,欢迎你说("没有."),并指出了解决"XML"问题的方法.
mon*_*jbl 52
我在网络服务器环境中处于类似情况.上传的典型大小约为150k行,从单个请求中消耗大量内存并不是一件好事.Apache POI Streaming 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());
}
}
Run Code Online (Sandbox Code Playgroud)
使用它有一些注意事项; 由于XLSX表的结构方式,并非所有数据都在流的当前窗口中可用.但是,如果您只是尝试从单元格中读取简单数据,那么它的效果非常好.
rjd*_*olb 11
可以通过使用File而不是Stream来改进内存使用.(最好使用流式API,但Streaming API有局限性,请参阅http://poi.apache.org/spreadsheet/index.html)
而不是
Workbook workbook = WorkbookFactory.create(inputStream);
Run Code Online (Sandbox Code Playgroud)
做
Workbook workbook = WorkbookFactory.create(new File("yourfile.xlsx"));
Run Code Online (Sandbox Code Playgroud)
这是根据:http://poi.apache.org/spreadsheet/quick-guide.html#FileInputStream
文件与InputStreams
"当打开工作簿时,无论是.xls HSSFWorkbook还是.xlsx XSSFWorkbook,都可以从File或InputStream加载工作簿.使用File对象可以降低内存消耗,而InputStream需要更多内存,因为它有缓冲整个文件."
Apache POI,HSSF和XSSF中的Excel支持支持3种不同的模式.
一个是完整的DOM-Like内存"UserModel",它支持读写.使用通用的SS(SpreadSheet)接口,您可以基本透明地编写HSSF(.xls)和XSSF(.xlsx).但是,它需要大量的内存.
POI还支持流式只读方式来处理文件EventModel.这比UserModel低得多,让您非常接近文件格式.对于HSSF(.xls),您可以获得一系列记录,并可选择一些处理它们的帮助(缺少单元格,格式跟踪等).对于XSSF(.xlsx),您可以从文件的不同部分获取SAX事件流,帮助获取文件的正确部分,并且还可以轻松处理文件的常见但小部分.
仅对于XSSF(.xlsx),POI还支持只写流式写入,适用于低级别但低内存写入.它主要只是支持新文件(某些类型的附加是可能的).没有HSSF等价物,并且由于许多记录中的来回字节偏移和索引偏移,所以很难做到......
对于您的具体情况,如您的澄清评论中所述,我认为您将要使用XSSF EventModel代码.见的POI文件上手,然后尝试寻找这 3 类中的POI和提卡它使用它的更多细节.
POI现在包含这些案例的API.SXSSF http://poi.apache.org/spreadsheet/index.html 因此它可以让你处理这些文件时,它不会加载在内存中的所有.
注意:我已经读过SXSSF作为写入API.加载应该使用XSSF完成而无需输入流文件(以避免在内存中完全加载它)
检查这个帖子。我展示了如何使用 SAX 解析器来处理 XLSX 文件。
简而言之,我org.xml.sax.helpers.DefaultHandler为 XLSX filez扩展了 whih 处理 XML 结构。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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后我解析 XML presending 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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
77535 次 |
| 最近记录: |