1, 简介
Excel2003(.xls) 是 Microsoft Excel2003 之前版本要求的格式, POI 提供两种方式读取这种类型的文件
1, 用户模式 (User API): 将 xls 文件全部读进内存, 然后以 dom 结构处理;
2, 事件模式 (event API): 用流的形式读取文件, 占用内存较少, 适用于愿意学习低级 API 结构的开发人员, 需要对 Excel 文件的各个部分有基本的了解.
2,XLS 文件介绍
XLS 后缀的文件包括 Worksheet 文档和 Workbook 文档两种. 是 Excel 4.0 及以前版本为 Worksheet 文档; 以后的版本为 Workbook 文档.
2.1,Worksheet Document
Worksheet 文档只包括一个 sheet, 默认的文件后缀是 "XLS".
2.2,Workbook Document
Workbook 文档可以包含多个 sheet, 每个 Workbook 文档都包含一个全局设置, 叫做 (workbook globals).
3,OLE 2 复合文档系统 - POIFS
就像上面展示的, xls 实际上实际上以复合文档的形式组织在一起; 然后 POI 以流的形式读取.
3.1,Workbook 文件流 (Workbook Streams)
Workbook 文件流会先读取 workbook globals Substream, 然后再依次读取每个 Sheet Substream.
这里展示的是 Workbook 文档的文件流结构. 鉴于我们基本不会遇到 Worksheet Document, 下面将只针对 Workbook Document 来讨论. 更多相关信息请参见文末参考链接.
3.2,Workbook Records
文件中的各种流会以 Record 的形式被解析, 每个 Record 都包含特定的数据和格式等相关信息.
例如 BOFRecord 记录了 Workbook 或 Sheet 的开始, EOFRecord 记录了 Workbook 或 Sheet 的结束等等...
org.apache.poi.hssf.record 包下面包括了各种 Record 类, 我们需要的数据和文档结构就包含在各个 Record 类中.
我们常用的 Record 差不多有下面这些:
- // 记录了 sheetName
- BoundSheetRecord
- // Workbook,Sheet 的开始
- BOFRecord
- // 存在单元格样式的空单元格
- BlankRecord
- // 布尔或错误单元格
- BoolErrRecord
- // 公式单元格
- FormulaRecord
- // 公式的计算结果单元格
- StringRecord
- // 文本单元格
- LabelRecord
- // 共用的文本单元格
- LabelSSTRecord
- // 数值单元格: 数字单元格和日期单元格
- NumberRecord
- // Workbook,Sheet 的结束
- EOFRecord
4, 解析文件
想要利用事件模式的 API, 需要将文件用 FileSystem 将文件读取进来
1, 继承 HSSFListener 接口, 创建自己的监听器 listener;
利用 recordsid,recordsid 是 org.apache.poi.hssf.record 中的类中包含的静态引用号 (例如 BOFRecord.sid)
2, 使用 HSSFRequest.addListener(yourlistener,recordsid) 注册监听器, 也可以用 HSSFRequest.addListenerForAllRecords(mylistener) 添加全部监听器;
3, 构造 org.apache.poi.poifs.filesystem.FileSystem 的实例并将其传递给 XLS 文件输入流;
4, 将输入流 DocumentInputStream 解析成 record;
5, 根据注册的监听类型分别处理各种类型的 record;
4.1,org.apache.poi.hssf.eventusermodel.HSSFEventFactory
事件模式常用的方法一般是下面两个:
- /**
- * 将一个文件处理为基本的 Record 事件
- * @param req 一个 HSSFRequest 实例, 记录了 Record 的所有监听器
- * @param fs 包含 WorkBook 的 POIFS 文件系统
- */
- HSSFEventFactory.processWorkbookEvents(HSSFRequest req, POIFSFileSystem fs)
- /**
- * 将一个文件处理为基本的 Record 事件
- * @param req 一个 HSSFRequest 实例, 记录了 Record 的所有监听器
- * @param in 包含 WorkBook 的 DirectoryNode 的输入流
- */
- HSSFEventFactory.processEvents(HSSFRequest req, InputStream in)
5, 事件模式实例
这个例子是参照 POI 官网提供的代码, 针对的是 POI 最新的版本 POI 4.0.1; 但我们常用的 3.x 也基本都可以正常运行
- EventExample.class
- /**
- * 此示例显示如何使用事件 API 读取文件
- */
- public class EventExample implements HSSFListener {
- private SSTRecord sstrec;
- /**
- * 此方法监听传入记录并根据需要处理它们
- * @param record 读取时找到的记录
- */
- public void processRecord(Record record) {
- switch (record.getSid()) {
- //BOFRecord 可以表示工作表或工作簿的开头
- case BOFRecord.sid:
- BOFRecord bof = (BOFRecord) record;
- if (bof.getType() == bof.TYPE_WORKBOOK) {
- System.out.println("监听到工作表");
- } else if (bof.getType() == bof.TYPE_WORKSHEET) {
- System.out.println("监听到工作簿");
- }
- break;
- case BoundSheetRecord.sid:
- BoundSheetRecord bsr = (BoundSheetRecord) record;
- System.out.println("工作簿名称:" + bsr.getSheetname());
- break;
- case RowRecord.sid:
- RowRecord rowrec = (RowRecord) record;
- System.out.println("监听到行, 第一行位于"
- + rowrec.getFirstCol() + "最后一行位于" + rowrec.getLastCol());
- break;
- case NumberRecord.sid:
- NumberRecord numrec = (NumberRecord) record;
- System.out.println("发现单元格:" + numrec.getValue()
- + "位于" + numrec.getRow() + "行," + numrec.getColumn() + "列" );
- break;
- case LabelSSTRecord.sid:
- LabelSSTRecord lrec = (LabelSSTRecord) record;
- System.out.println("找到文本值:"
- + sstrec.getString(lrec.getSSTIndex()));
- break;
- }
- }
- /**
- * 读取 Excel 文件, 并打印出文件内容
- * @param args 要读取的文件
- * @throws IOException
- */
- public static void main(String[] args) throws IOException {
- // 使用输入的文件创建一个新的文件输入流
- FileInputStream fin = new FileInputStream(args[0]);
- // 创建一个新的 org.apache.poi.poifs.filesystem.Filesystem
- POIFSFileSystem poifs = new POIFSFileSystem(fin);
- // 在 InputStream 中获取 Workbook 流
- InputStream din = poifs.createDocumentInputStream("Workbook");
- // 构造出 HSSFRequest 对象
- HSSFRequest req = new HSSFRequest();
- // 注册全部的监听器
- req.addListenerForAllRecords(new EventExample());
- // 创建事件工厂
- HSSFEventFactory factory = new HSSFEventFactory();
- // 根据文档输入流处理我们监听的事件
- factory.processEvents(req, din);
- // 关闭文件输入流
- fin.close();
- // 关闭文档输入流
- din.close();
- System.out.println("读取结束");
- }
- }
6, 后记
本文主要介绍了利用 POI 读取 xls 文件, xls 文件因为是 Excel2003 以前的版本都使用的格式, 其实不同版本之间还是有一些不同的, 如果遇到了奇怪的问题不妨考虑一下版本的问题. 这篇文章没有涉及 d 的实用部分, 会在之后的文章中写出来.
参考链接
Apache POI 官网: https://poi.apache.org/
XLS 规范 [PDF]: http://www.openoffice.org/sc/excelfileformat.pdf
来源: https://yq.aliyun.com/articles/690473