POI Event API是如何从Excel读取数据的?为什么它使用较少的RAM



我目前正在撰写我的学士论文,我正在使用Apache的POI Event API。简而言之,我的工作是关于一种从Excel中读取数据的更有效的方法。

开发人员一次又一次地问我,这对Event API到底意味着什么。不幸的是,我在Apache页面上找不到任何关于基本原理的内容。

下面的代码介绍了我如何使用POI事件API(这来自XSSF和SAX的Apache示例):

import java.io.InputStream;
import java.util.Iterator;
import org.apache.poi.ooxml.util.SAXHelper;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.ParserConfigurationException;
public class ExampleEventUserModel {
public void processOneSheet(String filename) throws Exception {
OPCPackage pkg = OPCPackage.open(filename);
XSSFReader r = new XSSFReader( pkg );
SharedStringsTable sst = r.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
// To look up the Sheet Name / Sheet Order / rID,
//  you need to process the core Workbook stream.
// Normally it's of the form rId# or rSheet#
InputStream sheet2 = r.getSheet("rId2");
InputSource sheetSource = new InputSource(sheet2);
parser.parse(sheetSource);
sheet2.close();
}
public void processAllSheets(String filename) throws Exception {
OPCPackage pkg = OPCPackage.open(filename);
XSSFReader r = new XSSFReader( pkg );
SharedStringsTable sst = r.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
Iterator<InputStream> sheets = r.getSheetsData();
while(sheets.hasNext()) {
System.out.println("Processing new sheet:n");
InputStream sheet = sheets.next();
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource);
sheet.close();
System.out.println("");
}
}
public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException, ParserConfigurationException {
XMLReader parser = SAXHelper.newXMLReader();
ContentHandler handler = new SheetHandler(sst);
parser.setContentHandler(handler);
return parser;
}
/**
* See org.xml.sax.helpers.DefaultHandler javadocs
*/
private static class SheetHandler extends DefaultHandler {
private SharedStringsTable sst;
private String lastContents;
private boolean nextIsString;
private SheetHandler(SharedStringsTable sst) {
this.sst = sst;
}
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
// c => cell
if(name.equals("c")) {
// Print the cell reference
System.out.print(attributes.getValue("r") + " - ");
// Figure out if the value is an index in the SST
String cellType = attributes.getValue("t");
if(cellType != null && cellType.equals("s")) {
nextIsString = true;
} else {
nextIsString = false;
}
}
// 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 = sst.getItemAt(idx).getString();
nextIsString = false;
}
// v => contents of a cell
// Output after we've seen the string contents
if(name.equals("v")) {
System.out.println(lastContents);
}
}
public void characters(char[] ch, int start, int length) {
lastContents += new String(ch, start, length);
}
}
public static void main(String[] args) throws Exception {
ExampleEventUserModel example = new ExampleEventUserModel();
example.processOneSheet(args[0]);
example.processAllSheets(args[0]);
}
}

有人能向我解释一下事件API是如何工作的吗?它是与基于事件的体系结构相同,还是其他什么?

*.xlsx文件是存储在Office Open XML中的Excel,也是apache poi作为XSSF处理的文件,它是一个ZIP归档文件,包含目录结构中XML文件中的数据。因此,我们可以解压缩*.xlsx文件,然后直接从XML文件中获取数据。

有一个包含所有字符串单元格值的/xl/sharedStrings.xml,还有一个描述工作簿结构的/xl/workbook.xml。还有CCD_ 11,它们正在存储纸张的数据。并且存在具有用于片材中的所有单元的样式设置的CCD_ 12。

默认情况下,在创建XSSFWorkbook时,*.xlsx文件的所有这些部分都将成为对象表示,如XSSFWorkbookXSSFSheetXSSFRowXSSFCell。。。以及存储器中的CCD_ 19的其他对象。

为了了解XSSFSheetXSSFRowXSSFCell的内存消耗情况,最好了解一下这些来源。这些对象中的每一个都包含多个作为内部成员的ListMap,当然还有多个方法。现在想象一下,一张纸有几十万行,每行包含数百个单元格。这些行和单元中的每一个将由存储器中的XSSFRowXSSFCell表示。这不能指责apache poi,因为如果需要处理这些对象,那么这些对象是必要的。但是,如果真正需要的只是从Excel表中获取内容,那么这些对象并不是全部必需的。这就是XSSF和SAX(事件API)方法的原因。

因此,如果只需要从表中读取数据,则可以简单地解析所有/xl/worksheets/sheet[n].xml文件的XML,而不需要为每个表、每行和这些表中的每个单元创建消耗内存的对象。

在基于事件的模式下解析XML意味着代码从上到下遍历XML,并定义了回调方法,如果代码检测到元素的开始、元素的结束或元素中的字符内容,就会调用这些方法。然后,适当的回调方法处理对元素的开始、结束或字符内容执行的操作。因此,读取XML文件只意味着从上到下遍历该文件一次,处理事件(元素的开始、结束、字符内容),并能够从中获取所有需要的内容。因此,内存消耗减少到存储从XML获得的文本数据。

XSSF和SAX(事件API)使用SheetHandler类,该类为此扩展了DefaultHandler。

但是,如果我们已经达到了获取底层XML数据并对其进行处理的水平,那么我们也可以后退一步。原生CCD_ 37能够处理CCD_ 38并解析CCD_。因此,我们甚至根本不需要额外的图书馆。看看如何在java中读取超过100000行的excel文件?我在那里展示了这一点。我的代码使用Packagejavax.xml.stream,它还提供了使用基于事件的XMLEventReader,但不使用回调,而是使用线性代码。也许这段代码更容易理解,因为它是一体的。

为了检测数字格式是否是日期格式,因此格式化的单元格包含日期/时间值,使用一个单独的apache poiorg.apache.poi.ss.usermodel.DateUtil。这样做是为了简化代码。当然,即使是在这个课堂上,我们也可以对自己进行编码。

最新更新