五、通过eventusermodel读取文件
通过eventusermodel读取文件要比使用usermodel复杂得多,但效率也要高不少,因为它要求应用程序一边读取数据,一边处理数据。
eventusermodel实际上模拟了dom环境下sax处理xml文档的办法,应用程序首先要注册期望处理的数据,eventusermodel将在遇到匹配的数据结构时回调应用程序注册的方法。使用eventusermodel最大的困难在于你必须熟悉excel工作簿的内部结构。
在hssf中,低层次的二进制结构称为记录(record)。记录有不同的类型,每一种类型由org.apache.poi.hssf.record包中的一个java类描述。例如,bofrecord记录表示workbook或sheet区域的开始,rowrecord表示有一个行存在并保存其样式信息。
所有具有cellvaluerecordinterface接口的记录表示excel的单元格,包括numericrecord、labelsstrecord和formularecord(还有其他一些,其中部分已被弃置不用,部分用于优化处理,但一般而言,hssf可以转换它们)。
下面是一个注册事件处理句柄的例子:
private eventrecordfactory factory = new eventrecordfactory();
factory.registerlistener(new erflistener() {
public boolean processrecord(record rec) {
(got bof record);
return true;
}
}, new short[] {bofrecord.sid});
factory.processrecords(someinputstream);
六、hssf电子表格结构
如前所述,hssf建立在poifs的基础上。具体地说,excel 97+文件是ole 2复合文档( ole 2 compound document),底层的ole 2复合文档保存了一个总是命名为workbook(excel 95除外,hssf不支持excel 95)的流。
然而,宏和图片并不保存在workbook流,它们有自己独立的流,有时甚至会放到ole 2 cdf文件之内的另一个目录。理想情况下,宏也应该被保留,不过目前poi项目中还没有合适的api来处理宏。
每一个流之内是一组记录,一个记录其实就是一个字节数组,可分为记录头、记录体两部分。记录头指明了记录的类型(也即id)以及后继数据的长度,记录体被分割成多个字段(field),字段包含数值数据(包括对其他记录的引用)、字符数据或标记。
excel工作簿的顶级结构:
bla.xls {
ole2cdf headers
"workbook" stream {
workbook {
static string table record..
sheet names… and pointers
}
sheet {
row
row
…
number record (cell)
labelsst record (cell)
…
}
sheet
}
}
… images, macros, etc.
document summary
summary
七、通过hpsf读取文档属性
在microsoft word、excel、powerpoint等软件中,用户可以通过“文件”→“属性”菜单给文档添加附加信息,包括文档的标题、主题、摘要、类别、关键词等,同时应用软件本身还会加入最后访问的用户、最后访问和修改/打印的日期时间等信息。
文档的属性和正文是分开保存的。如前所述,ole 2 cdf文件内部就象是一个容器,里面包含许多类似目录和文件的结构,而poifs就是用来访问其中的文件的工具。这些文件也称为流,文档的属性就保存在poifs文件系统中专用的流里面。
以一个word文档为例:虽然在资源管理器中你只看到一个叫做myfile.doc的文档,其实在这个文档的内部,又包含了一个worddocument、一个summaryinformation和一个documentsummaryinformation文档;通常还会有其他的文档,这里暂且不管。
你能够猜出这些文档(流)分别包含什么内容吗?不错,worddocument包含了你在word里面编辑的文本,文档的属性保存在summaryinformation和documentsummaryinformation流里面。也许将所有属性保存在单个文档里面看起来太简单了,所以microsoft决心要使用两个流,为了使事情更复杂一点,这两个流的名字前面还加上了八进制的/005字符??这是一个不可打印的字符,因此前面就把它省略了。
microsoft定义的标准属性有一个好处,它们并不在乎主文档到底是什么类型??不管是word文档、excel工作簿还是powerpoint幻灯。只要你知道如何读取excel文档的属性,就知道了如何读取其他文档的属性。
读取文档属性其实并不复杂,因为java程序可以利用poi项目的hpsf包。hpsf是 horrible property set format的缩写,译成中文就是“讨厌的属性集格式”。hpsf包是poi项目实现的读取属性工具,目前还不支持属性写入。
对于读取microsoft定义的标准属性,通过hpsf提供的api可以很方便地办到;但如果要读取任意属性集就要用到更一般化的api,可以想象它要比读取标准属性的api复杂不少。本文只介绍读取标准属性的简单api,因为对大多数应用程序来说这已经完全足够了。
下面就是一个读取ole 2 cdf文档的标题(title)属性的java程序:
import java.io.*;
import org.apache.poi.hpsf.*;
import org.apache.poi.poifs.eventfilesystem.*;
/**
* 读取ole 2文档标题的示例程序,
* 在命令行参数中指定文档的文件名字。
*/
public class readtitle
{
public static void main(string[] args) throws ioexception
{
final string filename = args[0];
poifsreader r = new poifsreader();
r.registerlistener(new mypoifsreaderlistener(),
"/005summaryinformation");
r.read(new fileinputstream(filename));
}
static class mypoifsreaderlistener
implements poifsreaderlistener
{
public void processpoifsreaderevent(poifsreaderevent event)
{
summaryinformation si = null;
try
{
si = (summaryinformation)
propertysetfactory.create(event.getstream());
}
catch (exception ex)
{
throw new runtimeexception
("属性集流/"" + event.getpath() +
event.getname() + "/": " + ex);
}
final string title = si.gettitle();
if (title != null)
system.out.println("标题: /"" + title + "/"");
else
system.out.println("该文档没有标题.");
}
}
}
闽公网安备 35060202000074号