POI事件模式指北(一)-Excel2003

POI事件模式指北(一)-Excel2003

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”。

POI事件模式指北(一)-Excel2003

2.2、Workbook Document

Workbook文档可以包含多个sheet,每个Workbook文档都包含一个全局设置,叫做(workbook globals)。

POI事件模式指北(一)-Excel2003

3、OLE 2复合文档系统 - POIFS

就像上面展示的,xls实际上实际上以复合文档的形式组织在一起;然后POI以流的形式读取。

3.1、Workbook文件流 (Workbook Streams)

Workbook文件流会先读取workbook globals Substream,然后再依次读取每个Sheet Substream。

POI事件模式指北(一)-Excel2003

这里展示的是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

上一篇:POI事件模式指北(二)-Excel2007


下一篇:POI事件模式指北(三)- 读取Excel实战