使用Jacob来处理Word文档
Word或Excel程序是以一种COM组件形式存在的。如果能够在Java中调用Word的COM组件,就
能使用它的方法来获取Word文档中的文本信息。目前网上有许多提供这样的工具。
1 Jacob的下载
Jacob 是Java-COM Bridge的缩写,它在Java与微软的COM组件之间构建一座桥梁。使用Jacob
自带的DLL动态链接库,并通过JNI的方式实现了在Java平台上对COM程序的调用。Jacob下载
的地址为:
http://sourceforge.net/project/showfiles.php? group_id=109543&package_id=118368。本书
采用的是jacob_1.11_zip。解压下载的 Jacob_1.11_zip文件后,
2 在Eclipse中配置
(1)将jacob.jar导入工程的Build Path,然后确认自己机器的CPU类型(X86或AMD64),并
选择不同目录下的jacob.dll文件。
(2)将jacob.dll放到% JAVA_HOME%/jre/bin目录下,其中,%JAVA_HOME%就是JDK的安装
目录。注意这个的jre目录必须是Eclipse当前正在使 用的目录,在Eclipse中选择“window-
>Preferences”菜单,在弹出的对话框中选择“Java->Installed JREs”项,
(3)当前选择的JRE是“C:/Program Files/Java/jdk1.5.0_07/jre”目录下的,所以需要把
jacob.dll复制到“C:/Program Files/Java/jdk1.5.0_07/jre/bin”目录下面。
(4)在工程中新建一个ch7.jacob包, 并在包中创建WordReader类。该类将提供一个静态的
extractDoc()方法。它接收两个参数,一个是要处理的DOC文件名,另一个则是输出 的文件名
,然后通过JNI调用Word的API转换内容,该函数的代码如下。
public static void extractDoc(String inputFIle, String outputFile) {
boolean flag = false;
// 打开Word应用程序
ActiveXComponent app = new ActiveXComponent("Word.Application");
try {
// 设置word不可见
app.setProperty("Visible", new Variant(false));
// 打开word文件
Dispatch doc1 = app.getProperty("Documents").toDispatch();
Dispatch doc2 = Dispatch.invoke(
doc1,
"Open",
Dispatch.Method,
new Object[] { inputFIle, new Variant(false),
new Variant(true) }, new int[1]).toDispatch();
// 作为txt格式保存到临时文件
Dispatch.invoke(doc2, "SaveAs", Dispatch.Method, new Object[] {
outputFile, new Variant(7) }, new int[1]);
// 关闭word
Variant f = new Variant(false);
Dispatch.call(doc2, "Close", f);
flag = true;
} catch (Exception e) {
e.printStackTrace();
} finally {
app.invoke("Quit", new Variant[] {});
}
if (flag == true) {
System.out.println("Transformed Successfully");
} else {
System.out.println("Transform Failed");
}
}
(5)创建一个main函数来测试WordReader类,该main函数代码如下。
public static void main(String[] args) {
WordReader.extractDoc("c:/test.doc","c:/jacob.txt");
}
(6)新生成的txt文件被保存到c:/jacob.txt下
在使用Jacob时,很重要的一点是,用户本地系统中必须安装有Word的应用程序。否则也就无
法建立Java-COM桥,进而无法解析了。
解决jacob调用word处理doc文件的内存溢出问题
背景:
有个项目在系统后台用Jacob调用Word进程的API做doc文件处理。在进行压力测试的时候,发现运行一段时间后,内存占用奇高,大约7.4G。由于JVM占用内存限制为2G,因此怀疑多出来的内存应当是Jacob泄露的。
在网路上搜索解决办法,都是要这样调用
[java] view plaincopyprint?ComThread.InitSTA();
// do something
ComThread.Release();
ComThread.InitSTA();
// do something
ComThread.Release();
但是在项目中,使用了线程池进行DOC文件处理,也就是同一时间,会有多个WORD进程在跑,使用ComThread.InitSTA();之后,Jacob仅允许线程池里面的一个线程执行,其他线程都被锁住。
最后修改成ComThread.InitMTA(true);来初始化,经过24小时压力测试,可以同时有多个WORD进程运行,另外也解决了Jacob内存溢出问题。
使用JACOB进行Word编程示例
import java.util.ArrayList;
import java.util.List;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import org.apache.log4j.Logger;
/**
*
*作用:利用jacob插件生成word 文件!
*
*
*/
public class DOCWriter {
/** 日志记录器 */
static private Logger logger = Logger.getLogger(DOCWriter.class);
/** word文档
*
* 在本类中有两种方式可以进行文档的创建,<br>
* 第一种调用 createNewDocument
* 第二种调用 openDocument
*/
private Dispatch document = null;
/** word运行程序对象 */
private ActiveXComponent word = null;
/** 所有word文档 */
private Dispatch documents = null;
/**
* Selection 对象 代表窗口或窗格中的当前所选内容。 所选内容代表文档中选定(或突出
显示)的区域,如果文档中没有选定任何内容,则代表插入点。
* 每个文档窗格只能有一个Selection 对象,并且在整个应用程序中只能有一个活动的
Selection 对象。
*/
private Dispatch selection = null;
/**
*
* Range 对象 代表文档中的一个连续区域。 每个 Range 对象由一个起始字符位置和一个
终止字符位置定义。
* 说明:与书签在文档中的使用方法类似,Range 对象在 Visual Basic 过程中用来标识文
档的特定部分。
* 但与书签不同的是,Range对象只在定义该对象的过程运行时才存在。
* Range对象独立于所选内容。也就是说,您可以定义和处理一个范围而无需更改所选内
容。还可以在文档中定义多个范围,但每个窗格中只能有一个所选内容。
*/
private Dispatch range = null;
/**
* PageSetup 对象 该对象包含文档的所有页面设置属性(如左边距、下边距和纸张大小)
。
*/
private Dispatch pageSetup = null;
/** 文档中的所有表格对象 */
private Dispatch tables = null;
/** 一个表格对象 */
private Dispatch table = null;
/** 表格所有行对象 */
private Dispatch rows = null;
/** 表格所有列对象 */
private Dispatch cols = null;
/** 表格指定行对象 */
private Dispatch row = null;
/** 表格指定列对象 */
private Dispatch col = null;
/** 表格中指定的单元格 */
private Dispatch cell = null;
/** 字体 */
private Dispatch font = null;
/** 对齐方式 */
private Dispatch alignment = null;
/**
* 构造方法
*/
public DOCWriter() {
if(this.word == null){
/* 初始化应用所要用到的对象实例 */
this.word = new ActiveXComponent("Word.Application");
/* 设置Word文档是否可见,true-可见false-不可见 */
this.word.setProperty("Visible",new Variant(true));
/* 禁用宏 */
this.word.setProperty("AutomationSecurity", new Variant(3));
}
if(this.documents == null){
this.documents = word.getProperty("Documents").toDispatch();
}
}
/**
* 设置页面方向和页边距
*
* @param orientation
* 可取值0或1,分别代表横向和纵向
* @param leftMargin
* 左边距的值
* @param rightMargin
* 右边距的值
* @param topMargin
* 上边距的值
* @param buttomMargin
* 下边距的值
*/
public void setPageSetup(int orientation, int leftMargin,int rightMargin, int topMargin, int
buttomMargin) {
logger.debug("设置页面方向和页边距...");
if(this.pageSetup == null){
this.getPageSetup();
}
Dispatch.put(pageSetup, "Orientation", orientation);
Dispatch.put(pageSetup, "LeftMargin", leftMargin);
Dispatch.put(pageSetup, "RightMargin", rightMargin);
Dispatch.put(pageSetup, "TopMargin", topMargin);
Dispatch.put(pageSetup, "BottomMargin", buttomMargin);
}
/**
* 打开文件
*
* @param inputDoc
* 要打开的文件,全路径
* @return Dispatch
* 打开的文件
*/
public Dispatch openDocument(String inputDoc) {
logger.debug("打开Word文档...");
this.document = Dispatch.call(documents,"Open",inputDoc).toDispatch();
this.getSelection();
this.getRange();
this.getAlignment();
this.getFont();
this.getPageSetup();
return this.document;
}
/**
* 创建新的文件
*
* @return Dispache 返回新建文件
*/
public Dispatch createNewDocument(){
logger.debug("创建新的文件...");
this.document = Dispatch.call(documents,"Add").toDispatch();
this.getSelection();
this.getRange();
this.getPageSetup();
this.getAlignment();
this.getFont();
return this.document;
}
/**
* 选定内容
* @return Dispatch 选定的范围或插入点
*/
public Dispatch getSelection() {
logger.debug("获取选定范围的插入点...");
this.selection = word.getProperty("Selection").toDispatch();
return this.selection;
}
/**
* 获取当前Document内可以修改的部分<p><br>
* 前提条件:选定内容必须存在
*
* @param selectedContent 选定区域
* @return 可修改的对象
*/
public Dispatch getRange() {
logger.debug("获取当前Document内可以修改的部分...");
this.range = Dispatch.get(this.selection, "Range").toDispatch();
return this.range;
}
/**
* 获得当前文档的文档页面属性
*/
public Dispatch getPageSetup() {
logger.debug("获得当前文档的文档页面属性...");
if(this.document == null){
logger.warn("document对象为空...");
return this.pageSetup;
}
this.pageSetup = Dispatch.get(this.document, "PageSetup").toDispatch();
return this.pageSetup;
}
/**
* 把选定内容或插入点向上移动
* @param count 移动的距离
*/
public void moveUp(int count) {
logger.debug("把选定内容或插入点向上移动...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection,"MoveUp");
}
}
/**
* 把选定内容或插入点向下移动
* @param count 移动的距离
*/
public void moveDown(int count) {
logger.debug("把选定内容或插入点向下移动...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection,"MoveDown");
}
}
/**
* 把选定内容或插入点向左移动
* @param count 移动的距离
*/
public void moveLeft(int count) {
logger.debug("把选定内容或插入点向左移动...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection,"MoveLeft");
}
}
/**
* 把选定内容或插入点向右移动
* @param count 移动的距离
*/
public void moveRight(int count) {
logger.debug("把选定内容或插入点向右移动...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection,"MoveRight");
}
}
/**
* 回车键
*/
public void enterDown(int count){
logger.debug("按回车键...");
for(int i = 0;i < count;i++) {
Dispatch.call(this.selection, "TypeParagraph");
}
}
/**
* 把插入点移动到文件首位置
*/
public void moveStart() {
logger.debug("把插入点移动到文件首位置...");
Dispatch.call(this.selection,"HomeKey",new Variant(6));
}
/**
* 从选定内容或插入点开始查找文本
* @param selection 选定内容
* @param toFindText 要查找的文本
* @return boolean true-查找到并选中该文本,false-未查找到文本
*/
public boolean find(String toFindText) {
logger.debug("从选定内容或插入点开始查找文本"+" 要查找内容: "+toFindText);
/* 从selection所在位置开始查询 */
Dispatch find = Dispatch.call(this.selection,"Find").toDispatch();
/* 设置要查找的内容 */
Dispatch.put(find,"Text",toFindText);
/* 向前查找 */
Dispatch.put(find,"Forward","True");
/* 设置格式 */
Dispatch.put(find,"Format","True");
/* 大小写匹配 */
Dispatch.put(find,"MatchCase","True");
/* 全字匹配 */
Dispatch.put(find,"MatchWholeWord","True");
/* 查找并选中 */
return Dispatch.call(find,"Execute").getBoolean();
}
/**
* 把选定内容替换为设定文本
* @param selection 选定内容
* @param newText 替换为文本
*/
public void replace(String newText) {
logger.debug("把选定内容替换为设定文本...");
/* 设置替换文本 */
Dispatch.put(this.selection,"Text",newText);
}
/**
* 全局替换
* @param selection 选定内容或起始插入点
* @param oldText 要替换的文本
* @param replaceObj 替换为文本
*/
public void replaceAll(String oldText,Object replaceObj) {
logger.debug("全局替换...");
/* 移动到文件开头 */
moveStart();
/* 表格替换方式 */
String newText = (String) replaceObj;
/* 图片替换方式 */
if(oldText.indexOf("image") != -1 || newText.lastIndexOf(".bmp") != -1 ||
newText.lastIndexOf(".jpg") != -1 || newText.lastIndexOf(".gif") != -1){
while (find(oldText)) {
insertImage(newText);
Dispatch.call(this.selection,"MoveRight");
}
/* 正常替换方式 */
} else {
while (find(oldText)) {
replace(newText);
Dispatch.call(this.selection,"MoveRight");
}
}
}
/**
* 插入图片
* @param selection 图片的插入点
* @param imagePath 图片文件(全路径)
*/
public void insertImage(String imagePath) {
logger.debug("插入图片...");
Dispatch.call(this.selection, "TypeParagraph");
Dispatch.call(Dispatch.get(this.selection,"InLineShapes").toDispatch
(),"AddPicture",imagePath);
}
/**
* 合并表格
*
* @param selection 操作对象
* @param tableIndex 表格起始点
* @param fstCellRowIdx 开始行
* @param fstCellColIdx 开始列
* @param secCellRowIdx 结束行
* @param secCellColIdx 结束列
*/
public void mergeCell(int tableIndex, int fstCellRowIdx, int fstCellColIdx, int
secCellRowIdx, int secCellColIdx){
logger.debug("合并单元格...");
if(this.table == null){
logger.warn("table对象为空...");
return;
}
Dispatch fstCell = Dispatch.call(table, "Cell",new Variant(fstCellRowIdx), new Variant
(fstCellColIdx)).toDispatch();
Dispatch secCell = Dispatch.call(table, "Cell",new Variant(secCellRowIdx), new Variant
(secCellColIdx)).toDispatch();
Dispatch.call(fstCell, "Merge", secCell);
}
/**
* 想Table对象中插入数值<p>
* 参数形式:ArrayList<String[]>List.size()为表格的总行数<br>
* String[]的length属性值应该与所创建的表格列数相同
*
* @param selection 插入点
* @param tableIndex 表格起始点
* @param list 数据内容
*/
public void insertToTable(List<String[]> list){
System.out.println("向Table对象中插入数据...");
logger.debug("向Table对象中插入数据...");
if(list == null || list.size() <= 0){
logger.warn("写出数据集为空...");
return;
}
if(this.table == null){
logger.warn("table对象为空...");
return;
}
for(int i = 0; i < list.size(); i++){
String[] strs = list.get(i);
for(int j = 0; j<strs.length; j++){
/* 遍历表格中每一个单元格,遍历次数与所要填入的内容数量相同 */
Dispatch cell = this.getCell(i+1, j+1);
/* 选中此单元格 */
Dispatch.call(cell, "Select");
/* 写出内容到此单元格中 */
Dispatch.put(this.selection, "Text", strs[j]);
/* 移动游标到下一个位置 */
}
this.moveDown(1);
}
this.enterDown(1);
}
/**
* 在文档中正常插入文字内容
*
* @param selection 插入点
* @param list 数据内容
*/
public void insertToDocument(List<String> list){
logger.debug("向Document对象中插入数据...");
if(list == null || list.size() <= 0){
logger.warn("写出数据集为空...");
return;
}
if(this.document == null){
logger.warn("document对象为空...");
return;
}
for(String str : list){
/* 写出至word中 */
this.applyListTemplate(3, 2);
Dispatch.put(this.selection, "Text", str);
this.moveDown(1);
this.enterDown(1);
}
}
/**
* 创建新的表格
*
* @param selection 插入点
* @param document 文档对象
* @param rowCount 行数
* @param colCount 列数
* @param width 边框数值 0浅色1深色
* @return 新创建的表格对象
*/
public Dispatch createNewTable(int rowCount, int colCount, int width){
logger.debug("创建新的表格...");
if(this.tables == null){
this.getTables();
}
this.getRange();
if(rowCount > 0 && colCount > 0){
this.table = Dispatch.call(this.tables,"Add",this.range,new Variant(rowCount),new
Variant(colCount),new Variant(width)).toDispatch();
}
/* 返回新创建表格 */
return this.table;
}
/**
* 获取Document对象中的所有Table对象
*
* @return 所有Table对象
*/
public Dispatch getTables(){
logger.debug("获取所有表格对象...");
if(this.document == null){
logger.warn("document对象为空...");
return this.tables;
}
this.tables = Dispatch.get(this.document, "Tables").toDispatch();
return this.tables;
}
/**
* 获取Document中Table的数量
*
* @return 表格数量
*/
public int getTablesCount(){
logger.debug("获取文档中表格数量...");
if(this.tables == null){
this.getTables();
}
return Dispatch.get(tables, "Count").getInt();
}
/**
* 获取指定序号的Table对象
*
* @param tableIndex Table序列
* @return
*/
public Dispatch getTable(int tableIndex){
logger.debug("获取指定表格对象...");
if(this.tables == null){
this.getTables();
}
if(tableIndex >= 0){
this.table = Dispatch.call(this.tables, "Item", new Variant(tableIndex)).toDispatch();
}
return this.table;
}
/**
* 获取表格的总列数
*
* @return 总列数
*/
public int getTableColumnsCount() {
logger.debug("获取表格总行数...");
if(this.table == null){
logger.warn("table对象为空...");
return 0;
}
return Dispatch.get(this.cols,"Count").getInt();
}
/**
* 获取表格的总行数
*
* @return 总行数
*/
public int getTableRowsCount(){
logger.debug("获取表格总行数...");
if(this.table == null){
logger.warn("table对象为空...");
return 0;
}
return Dispatch.get(this.rows,"Count").getInt();
}
/**
* 获取表格列对象
*
* @return 列对象
*/
public Dispatch getTableColumns() {
logger.debug("获取表格行对象...");
if(this.table == null){
logger.warn("table对象为空...");
return this.cols;
}
this.cols = Dispatch.get(this.table,"Columns").toDispatch();
return this.cols;
}
/**
* 获取表格的行对象
*
* @return 总行数
*/
public Dispatch getTableRows(){
logger.debug("获取表格总行数...");
if(this.table == null){
logger.warn("table对象为空...");
return this.rows;
}
this.rows = Dispatch.get(this.table,"Rows").toDispatch();
return this.rows;
}
/**
* 获取指定表格列对象
*
* @return 列对象
*/
public Dispatch getTableColumn(int columnIndex) {
logger.debug("获取指定表格行对象...");
if(this.cols == null){
this.getTableColumns();
}
if(columnIndex >= 0){
this.col = Dispatch.call(this.cols, "Item", new Variant(columnIndex)).toDispatch();
}
return this.col;
}
/**
* 获取表格中指定的行对象
*
* @param rowIndex 行序号
* @return 行对象
*/
public Dispatch getTableRow(int rowIndex){
logger.debug("获取指定表格总行数...");
if(this.rows == null){
this.getTableRows();
}
if(rowIndex >= 0){
this.row = Dispatch.call(this.rows, "Item", new Variant(rowIndex)).toDispatch();
}
return this.row;
}
/**
* 自动调整表格
*/
public void autoFitTable() {
logger.debug("自动调整表格...");
int count = this.getTablesCount();
for (int i = 0; i < count; i++) {
Dispatch table = Dispatch.call(tables, "Item", new Variant(i + 1)).toDispatch();
Dispatch cols = Dispatch.get(table, "Columns").toDispatch();
Dispatch.call(cols, "AutoFit");
}
}
/**
* 获取当前文档中,表格中的指定单元格
*
* @param CellRowIdx 单元格所在行
* @param CellColIdx 单元格所在列
* @return 指定单元格对象
*/
public Dispatch getCell(int cellRowIdx, int cellColIdx) {
logger.debug("获取当前文档中,表格中的指定单元格...");
if(this.table == null){
logger.warn("table对象为空...");
return this.cell;
}
if(cellRowIdx >= 0 && cellColIdx >=0){
this.cell = Dispatch.call(this.table, "Cell", new Variant(cellRowIdx),new Variant
(cellColIdx)).toDispatch();
}
return this.cell;
}
/**
* 设置文档标题
*
* @param title 标题内容
*/
public void setTitle(String title){
logger.debug("设置文档标题...");
if(title == null || "".equals(title)){
logger.warn("文档标题为空...");
return;
}
Dispatch.call(this.selection, "TypeText", title);
}
/**
* 设置当前表格线的粗细
*
* @param width
* width范围:1<w<13,如果是0,就代表没有框
*/
public void setTableBorderWidth(int width) {
logger.debug("设置当前表格线的粗细...");
if(this.table == null){
logger.warn("table对象为空...");
return;
}
/*
* 设置表格线的粗细 1:代表最上边一条线 2:代表最左边一条线 3:最下边一条线 4
:最右边一条线 5:除最上边最下边之外的所有横线
* 6:除最左边最右边之外的所有竖线 7:从左上角到右下角的斜线 8:从左下角到右上
角的斜线
*/
Dispatch borders = Dispatch.get(table, "Borders").toDispatch();
Dispatch border = null;
for (int i = 1; i < 7; i++) {
border = Dispatch.call(borders, "Item", new Variant(i)).toDispatch();
if (width != 0) {
Dispatch.put(border, "LineWidth", new Variant(width));
Dispatch.put(border, "Visible", new Variant(true));
} else if (width == 0) {
Dispatch.put(border, "Visible", new Variant(false));
}
}
}
/**
* 对当前selection设置项目符号和编号
* @param tabIndex
* 1: 项目编号
* 2: 编号
* 3: 多级编号
* 4: 列表样式
* @param index
* 0:表示没有 ,其它数字代表的是该Tab页中的第几项内容
*/
public void applyListTemplate(int tabIndex,int index){
logger.debug("对当前selection设置项目符号和编号...");
/* 取得ListGalleries对象列表 */
Dispatch listGalleries = Dispatch.get(this.word, "ListGalleries").toDispatch();
/* 取得列表中一个对象 */
Dispatch listGallery = Dispatch.call(listGalleries, "Item", new Variant
(tabIndex)).toDispatch();
Dispatch listTemplates = Dispatch.get(listGallery, "ListTemplates").toDispatch();
if(this.range == null){
this.getRange();
}
Dispatch listFormat = Dispatch.get(this.range, "ListFormat").toDispatch();
Dispatch.call(listFormat,"ApplyListTemplate",Dispatch.call(listTemplates, "Item", new
Variant(index)), new Variant(true),new Variant(1),new Variant(0));
}
/**
* 增加文档目录
*
* 目前采用固定参数方式,以后可以动态进行调整
*/
public void addTablesOfContents()
{
/* 取得ActiveDocument、TablesOfContents、range对象 */
Dispatch ActiveDocument = word.getProperty("ActiveDocument").toDispatch();
Dispatch TablesOfContents = Dispatch.get
(ActiveDocument,"TablesOfContents").toDispatch();
Dispatch range = Dispatch.get(this.selection, "Range").toDispatch();
/* 增加目录 */
Dispatch.call(TablesOfContents,"Add",range,new Variant(true),new Variant(1),new
Variant(3),new Variant(true),new Variant(""),new Variant(true),new Variant(true));
}
/**
* 设置当前Selection 位置方式
* @param selectedContent 0-居左;1-居中;2-居右。
*/
public void setAlignment(int alignmentType) {
logger.debug("设置当前Selection 位置方式...");
if(this.alignment == null){
this.getAlignment();
}
Dispatch.put(this.alignment, "Alignment", alignmentType);
}
/**
* 获取当前选择区域的对齐方式
*
* @return 对其方式对象
*/
public Dispatch getAlignment(){
logger.debug("获取当前选择区域的对齐方式...");
if(this.selection == null){
this.getSelection();
}
this.alignment = Dispatch.get(this.selection, "ParagraphFormat").toDispatch();
return this.alignment;
}
/**
* 获取字体对象
*
* @return 字体对象
*/
public Dispatch getFont(){
logger.debug("获取字体对象...");
if(this.selection == null){
this.getSelection();
}
this.font = Dispatch.get(this.selection, "Font").toDispatch();
return this.font;
}
/**
* 设置选定内容的字体 注:在调用此方法前,选定区域对象selection必须存在
*
* @param fontName
* 字体名称,例如 "宋体"
* @param isBold
* 粗体
* @param isItalic
* 斜体
* @param isUnderline
* 下划线
* @param rgbColor
* 颜色,例如"255,255,255"
* @param fontSize
* 字体大小
* @param Scale
* 字符间距,百分比值。例如 70代表缩放为70%
*/
public void setFontScale(String fontName, boolean isBold, boolean isItalic, boolean
isUnderline, String rgbColor, int Scale, int fontSize) {
logger.debug("设置字体...");
Dispatch.put(this.font, "Name", fontName);
Dispatch.put(this.font, "Bold", isBold);
Dispatch.put(this.font, "Italic", isItalic);
Dispatch.put(this.font, "Underline", isUnderline);
Dispatch.put(this.font, "Color", rgbColor);
Dispatch.put(this.font, "Scaling", Scale);
Dispatch.put(this.font, "Size", fontSize);
}
/**
* 保存文件
* @param outputPath 输出文件(包含路径)
*/
public void saveAs(String outputPath) {
logger.debug("保存文件...");
if(this.document == null){
logger.warn("document对象为空...");
return;
}
if(outputPath ==null || "".equals(outputPath)){
logger.warn("文件保存路径为空...");
return;
}
Dispatch.call(this.document,"SaveAs",outputPath);
}
public void saveAsHtml(String htmlFile){
Dispatch.invoke(this.document,"SaveAs",Dispatch.Method, new Object[]{htmlFile,new
Variant(8)}, new int[1]);
}
/**
* 关闭文件
* @param document 要关闭的文件
*/
public void close() {
logger.debug("关闭文件...");
if(document == null){
logger.warn("document对象为空...");
return;
}
Dispatch.call(document,"Close",new Variant(0));
}
/**
* 列印word文件
*
*/
public void printFile(){
logger.debug("打印文件...");
if(document == null){
logger.warn("document对象为空...");
return;
}
Dispatch.call(document,"PrintOut");
}
/**
* 退出程序
*/
public void quit() {
logger.debug("退出程序");
word.invoke("Quit",new Variant[0]);
ComThread.Release();
}
public static void main(String args[]){
DOCWriter writer = new DOCWriter();
writer.createNewDocument();
List<String[]> listTable = new ArrayList<String[]>();
for(int i = 0 ; i<10; i++){
String str[] = new String[4];
for(int j = 0 ; j<4; j++){
str[j] = String.valueOf(j);
}
listTable.add(str);
}
writer.setFontScale("宋体", false, false,false, "1,1,1,1", 70, 14);
List<String> list = new ArrayList<String>();
list.add("忘记你我做不到");
list.add("不去天涯海角");
list.add("在我身边就好");
list.add("如果爱是痛苦的泥沼");
list.add("让我们一起逃");
list.add("忘记你我做不到");
list.add("不去天涯海角");
list.add("在我身边就好");
list.add("如果爱是痛苦的泥沼");
list.add("让我们一起逃");
writer.moveDown(3);
writer.setAlignment(0);
writer.setTitle("Test");
writer.insertImage("E://test.jpg");
writer.enterDown(1);
writer.insertToDocument(list);
writer.setFontScale("幼圆", true, true,true, "1,1,1,1", 70, 14);
writer.createNewTable(10, 5, 0);
writer.insertToTable(listTable);
writer.setFontScale("华文仿宋", true, true,false, "1,1,1,1", 70, 14);
writer.createNewTable(10, 5, 0);
writer.insertToTable(listTable);
writer.setFontScale("华文新魏", true, false,false, "100,1,1,1", 70, 14);
writer.insertToDocument(list);
// writer.saveAsHtml("E://test.html");
writer.saveAs("E://TestDocBenjamin.doc");
// writer.close();
}
}
cob操作word
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang.SystemUtils;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
/**
*
* 类: <code> WordUtils </code>
* 功能描述:
* 创建人: 王星乐
* 创建日期: 2013-8-2 上午10:02:23
* 开发环境: JDK6.0
*/
public class WordUtils {
/**
*
* 功能描述: 将word转换为pdf,注意此方法不会关闭is和os
* @param is 输入流
* @param os 输出流
*/
public static void convertToPdf(InputStream is, OutputStream os) {
ActionCallBack action = new ActionCallBack() {
@Override
public void execute(String srcFilePath, String destFilePath) {
int wdFormatPDF = 17;// PDF 格式
ActiveXComponent app = null;
Dispatch doc = null;
try {
app = new ActiveXComponent
("Word.Application");
app.setProperty("Visible", new Variant
(false));
Dispatch docs = app.getProperty
("Documents").toDispatch();
doc = Dispatch.call(docs, "Open",
srcFilePath).toDispatch();
Dispatch.call(doc, "SaveAs", destFilePath,
wdFormatPDF);
Dispatch.call(doc, "Close", false);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
if (app != null) {
app.invoke("Quit", new Variant[]
{});
}
ComThread.Release();
}
}
};
process(is, os, ".doc", ".pdf", action);
}
/**
*
* 功能描述: 将word转换为html,注意此方法不会关闭is和os
* @param is 输入流
* @param os 输出流
*/
public static void convertToHtml(InputStream is, String path) {
//临时文件目录
File tempDir = new File(SystemUtils.getJavaIoTmpDir().getAbsolutePath
(), "wordTemp");
if (!tempDir.exists()) {
tempDir.mkdir();
}
File srcFile = new File(tempDir, UUID.randomUUID().toString() + ".doc");
if (srcFile.exists()) {
srcFile.delete();
}
File destFile = new File(tempDir, path);
if (destFile.exists()) {
destFile.delete();
}
//从输入流生成src文件
OutputStream srcOs = null;
try {
srcOs = new BufferedOutputStream(new FileOutputStream
(srcFile));
copy(is, srcOs);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
close(srcOs);
}
int wdFormatHTML = 8;// HTML 格式
ActiveXComponent app = null;
Dispatch doc = null;
try {
app = new ActiveXComponent("Word.Application");
app.setProperty("Visible", new Variant(false));
Dispatch docs = app.getProperty("Documents").toDispatch();
doc = Dispatch.call(docs, "Open", srcFile.getAbsolutePath
()).toDispatch();
Dispatch.call(doc, "SaveAs", path, wdFormatHTML);
Dispatch.call(doc, "Close", false);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
if (app != null) {
app.invoke("Quit", new Variant[] {});
}
ComThread.Release();
}
}
/**
*
* 功能描述: 替换书签,注意此方法不会关闭is和os
* @param is 输入流
* @param os 输出流
* @param bookmarkList 书签集合
* @param dataList 替换内容集合
*/
public static void replaceBookmarks(InputStream is, OutputStream os, final
List<String> bookmarkList,
final List<String> dataList) {
ActionCallBack action = new ActionCallBack() {
@Override
public void execute(String srcFilePath, String destFilePath) {
ActiveXComponent app = null;
Dispatch doc = null;
try {
app = new ActiveXComponent
("Word.Application");
app.setProperty("Visible", new Variant
(false));
Dispatch documents = app.getProperty
("Documents").toDispatch();
doc = Dispatch.call(documents, "Open",
srcFilePath).toDispatch();
Dispatch activeDocument =
app.getProperty("ActiveDocument").toDispatch();
Dispatch bookMarks = Dispatch.call
(activeDocument, "Bookmarks").toDispatch();
//替换书签
for (int i = 0; i < bookmarkList.size(); i++)
{
boolean bookMarkExist =
Dispatch.call(bookMarks, "Exists", bookmarkList.get(i)).changeType(
Variant.VariantBoolean).getBoolean();
if (bookMarkExist == true) {
Dispatch rangeItem =
Dispatch.call(bookMarks, "Item", bookmarkList.get(i)).toDispatch();
Dispatch range =
Dispatch.call(rangeItem, "Range").toDispatch();
Dispatch.put(range,
"Text", new Variant(dataList.get(i)));
}
}
Dispatch.call(doc, "SaveAs", destFilePath);
Dispatch.call(doc, "Close", false);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
if (app != null) {
app.invoke("Quit", new Variant[]
{});
}
ComThread.Release();
}
}
};
process(is, os, ".doc", ".doc", action);
}
/**
*
* 功能描述: 模板方法,做初始化工作,调用回调函数,执行清理工作。
* @param is
* @param os
* @param srcFileExtension
* @param destFileExtension
* @param action
*/
public static void process(InputStream is, OutputStream os, String srcFileExtension,
String destFileExtension,
ActionCallBack action) {
//临时文件目录
File tempDir = new File(SystemUtils.getJavaIoTmpDir().getAbsolutePath
(), "wordTemp");
if (!tempDir.exists()) {
tempDir.mkdir();
}
File srcFile = new File(tempDir, UUID.randomUUID().toString() +
srcFileExtension);
if (srcFile.exists()) {
srcFile.delete();
}
File destFile = new File(tempDir, UUID.randomUUID().toString() +
destFileExtension);
if (destFile.exists()) {
destFile.delete();
}
//从输入流生成src文件
OutputStream srcOs = null;
try {
srcOs = new BufferedOutputStream(new FileOutputStream
(srcFile));
copy(is, srcOs);
} catch (Throwable e) {
throw new RuntimeException(e);
} finally {
close(srcOs);
}
action.execute(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
//拷贝dest文件到输出流
InputStream destIs = null;
try {
destIs = new BufferedInputStream(new FileInputStream
(destFile));
copy(destIs, os);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
close(destIs);
}
//删除临时文件
if (srcFile.exists()) {
srcFile.delete();
}
if (destFile.exists()) {
destFile.delete();
}
}
private interface ActionCallBack {
void execute(String srcFilePath, String destFilePath);
}
/**
*
* 功能描述: 将输入流的数据拷贝到输出流
* @param is
* @param os
* @throws IOException
*/
private static void copy(InputStream is, OutputStream os) throws IOException {
int len = 0;
byte[] buffer = new byte[1024 * 4];
while (-1 != (len = is.read(buffer))) {
os.write(buffer, 0, len);
}
}
/**
*
* 功能描述: 关闭流
* @param s
*/
private static void close(Closeable s) {
if (s != null) {
try {
s.close();
} catch (IOException e) {
//忽略
}
}
}
}
//0:Microsoft Word 97 - 2003 文档 (.doc)
//1:Microsoft Word 97 - 2003 模板 (.dot)
//2:文本文档 (.txt)
//3:文本文档 (.txt)
//4:文本文档 (.txt)
//5:文本文档 (.txt)
//6:RTF 格式 (.rtf)
//7:文本文档 (.txt)
//8:HTML 文档 (.htm)(带文件夹)
//9:MHTML 文档 (.mht)(单文件)
//10:MHTML 文档 (.mht)(单文件)
//11:XML 文档 (.xml)
//12:Microsoft Word 文档 (.docx)
//13:Microsoft Word 启用宏的文档 (.docm)
//14:Microsoft Word 模板 (.dotx)
//15:Microsoft Word 启用宏的模板 (.dotm)
//16:Microsoft Word 文档 (.docx)
//17:PDF 文件 (.pdf)
//18:XPS 文档 (.xps)
//19:XML 文档 (.xml)
//20:XML 文档 (.xml)
//21:XML 文档 (.xml)
//22:XML 文档 (.xml)
//23:OpenDocument 文本 (.odt)
//24:WTF 文件 (.wtf)
用Java操作Office 2007
转自:http://www.infoq.com/cn/articles/cracking-office-2007-with-java
Office 2007平台作为一个构建富客户端应用程序的基本平台,并通过不同的手段使用Java来进行互操作。 但是,有一个Office/Java互操作的方面没有考虑到,那就是使Office和Java共同工作,也就是说让Java应用程序来操作Office文档:比如创建文档,编辑文档,收集数据等等。
从以往看来,这其中经常会出现一些问题,这是由于Office文档(主要是Word,Excel和PowerPoint)是存储在一个二进制格式文件中,在COM中被称为结构化存储格式, 是一个通过COM接口的层次化二进制格式。 对COM开发者(或者其他使用COM相关语言的开发者,如Visual Basic, Delphi 和C++/ATL)而言非常方便,但产生的文件对于那些不能“讲COM”的语言是无法访问的。有许许多多的应用程序都是为了让Java语言可以访问这些文件的内容;比如大家都知道Excel可以读取逗号分隔符文件(CSV),因此,Java应用程序相应将数据导出到Excel友好的格式时一般会选用CSV格式(或是其他丑陋的格式)。Word则是可以读取富文本格式(RTF)文件,而RTF标准是公开和有详细文档的。Office的后来者,Office 2003,引入了一个新的XML格式(WordML),Java开发者可以用它来读写Office文档,但是这些格式并没有很好的文档,Java开发者频繁的发现自己是通过试错法来进行WordML格式的学习。 各种各样的开源项目都参与进来想要解决这个问题,比如Apache的POI框架,可以用来读写Excel文档,还有各种各样的Java-COM解决方案,这些解决方案一般倾向于使用和Office自己使用的结构化存储应用程序接口相同的应用程序接口进行Excel文档的读写,但很难满足需要,直到现在,开发者不得不指出Office文档格式的内部结构是一个非常复杂的结构,另外一点毋庸置疑的是它是一个没有完整文档的结构。
总体上来说,如果温和一点说的话,Java/Office的故事是一个非常讨厌的境况。对于Java的开发人员而言,他们要么一边嘴里说着“Office这种破东西怎么还会有人想去用它”一边用记忆里的伊索寓言来安慰自己,要么干脆告诉那些使用Office的客户由于Microsoft和Sun两家公司之间的诉讼,Java不能操作Office。
对于Office 2007来说,微软毫无疑问的迈出了解决这些问题的一大步。没有比原始的JDK更复杂的东西---也就是说并不要求使用一些第三方的库---Java应用程序现在可以读写任何Office 2007的文档,这是由于Office 2007文档现在使用的是XML文档的ZIP格式文件。 这种格式被称作“OpenMXL”规范并且已经被提交到欧洲计算机制造商协会(ECMA),这个协会同样拥有C#语言和CLI运行时规范,所有的OpenXML规范现在都可以被任何人*的从ECMA的网站下载。 除了这些,再安装好Office 2007(为了验证和作一些测试)和一个标准的Java6 JDK安装,Java现在可以打开任何的Office 2007文档,找出来文档中间的内容,操作它们,并且再次保存这些数据。
与上篇文章不同,在这篇文章中,除了创建一个简单的应用程序之外,代码将会使用一种首先由Stuart Halloway提出的、被称作探索测试(exploration testing)的技术。在一个探索测试中,开发者编写单元测试用来探索应用程序接口,使用单元测试世界中的断言验证结果的正确性。探索测试带来的好处是当一个新版本的应用程序接口可用时---在这个例子中,可能是一个新版本的Office---运行这些测试可以用来确认新版本的采用不会影响到原本对应用程序接口的使用。
对于初学者来说,让我们首先快速的了解一下Office 2007文档。首先看一个仅仅包含文本的Word 2007文档,就像下面一样:
当保存的时候,使用Word 2007将它保存为“Hello.docx”,除非你使用了向后兼容格式,比如说Office 2003的WordML格式,或者是更老的Word 97二进制结构化存储格式。“.docx”文件是OpenXML格式的,微软的文档中声称该格式是XML文档的ZIP压缩格式文件,这些文件中包含了文档中的数据和格式,存储的方式与之前的Office版本中的二进制结构化存储应用程序接口存储数据的方式有些类似。如果这是真的,那么使用Java中提供的用来处理ZIP和TAR格式的“jar”实用工具应该可以展示这些内容,而事实上,它的确可以:
Word 2007文档的基本格式已经非常明显了,仅仅通过控制台的输出就可以看到。(事实上,“jar”实用工具所展示的这激动人心的一切,说明java.util.jar和/或 java.util.zip包同样可以简单的访问这些内容。)几乎没有对规范作任何的破解,很明显,文档中的主要内容应该被存储到了“document.xml”文件中,剩余的其他XML文件则应该是各种各样的辅助部分,比如文档中应用到的字体(fontTable.xml)和使用到的Office主题(theme/theme1.xml),等等。
是时间来编写一些探索测试了。(我们鼓励感兴趣的读者打开一个文本编辑器或者集成开发环境,并将下面的内容填入你的JUnit 4测试类当中,并且扩展这些测试。) 使用JUnit 4,第一个测试是为了简单的确认文件在我们预想的位置(显然这是下面测试可以运行的一个必要的需求)。
@Test public void verifyFileIsThere() { assertTrue(new File("hello.docx").exists()); assertTrue(new File("hello.docx").canRead()); assertTrue(new File("hello.docx").canWrite()); }
下面的测试简单的验证了我们可以使用Java库中的java.util.zip.ZipFile来打开这个文件:
@Test public void openFile() throws IOException, ZipException { ZipFile docxFile = new ZipFile(new File("hello.docx")); assertEquals(docxFile.getName(), "hello.docx"); }
现在一切看来都非常不错。Java的ZipFile类正确的识别了我们的文件,一个zip文件,如果我们还能继续保持这样的运气,让我们继续我们的测试,来遍历一下,识别文档中的内容并找出其中的数据。让我们编写一个快速的测试来从“document.xml”文件中找出所有的内容。
@Test public void listContents() throws IOException, ZipException { boolean documentFound = false; ZipFile docxFile = new ZipFile(new File("hello.docx")); Enumeration entriesIter = docxFile.entries(); while (entriesIter.hasMoreElements()) { ZipEntry entry = entriesIter.nextElement(); if (entry.getName().equals("document.xml")) documentFound = true; } assertTrue(documentFound); }
令人诧异的是,当我们运行测试的时候,测试过程产生了一个失败;并没有找到“document.xml”文件,这是由于ZipFile/ZipEntry 应用程序接口需要压缩文件中完整的路径名称。将测试中的路径改为“word/document.xml”,测试就通过了。
很好,我们已经找到文件了,下面让我们打开这个文件看看XML里面是什么。这非常简单,因为ZipFile有一个返回ZipEntry的应用程序接口。
@Test public void getDocument() throws IOException, ZipException { ZipFile docxFile = new ZipFile(new File("hello.docx")); ZipEntry documentXML = docxFile.getEntry("word/document.xml"); assertNotNull(documentXML); }
ZipFile代码可以返回它包含的实体内容,通过调用getInputStream()方法即可,不要对InputStream产生任何怀疑。将InputStream发送到一个DOM节点中就可以创建一个关于该文档的DOM。
@Test public void fromDocumentIntoDOM() throws IOException, ZipException, SAXException, ParserConfigurationException { ZipFile docxFile = new ZipFile(new File("hello.docx")); ZipEntry documentXML = docxFile.getEntry("word/document.xml"); InputStream documentXMLIS = docxFile.getInputStream(documentXML); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); Document doc = dbf.newDocumentBuilder().parse(documentXMLIS); assertEquals("[w:document: null]", doc.getDocumentElement().toString()); }
事实上,与其他支持各种Word所需格式的XML文档相比,document.xml文件的内容(为了明显起见,将命名空间声明等内容去除)看起来也相当乏味:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <w:document ...> <w:body> <w:p w:rsidR="00DE36E5" w:rsidRDefault="00DE36E5"> <w:r> <w:t>Hello, from Office 2007!</w:t> </w:r> </w:p> <w:sectPr w:rsidR="00DE36E5"> <w:pgSz w:w="12240" w:h="15840"/> <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="720" w:footer="720" w:gutter="0"/> <w:cols w:space="720"/> <w:docGrid w:linePitch="360"/> </w:sectPr> </w:body> </w:document>
关于文档中各个元素具体代表什么内容的细节已经超出了这篇文章的讨论范围,读者可以查阅OpenXML文档的具体内容来获得参考,但是文档中的主要内容是十分明显的。比如说文档中包括“p”元素(段落),包括“r”元素(文本区),包括“t”元素(文本),在本例的hello.docx文档中,单句“Hello from Office 2007”就是由这些元素构成的。
读过文件的内容后,现在可以来修改这些内容了,将其写到文件中,并用Word 2007打开它。快速的查看ZipFile和ZipEntry的应用程序接口可以发现这样一个问题:尽管这些类可以用来读取一个zip文件,但它们并不能写入或创建它们。
有很多可用的方法可以用于解决这个问题。一个简单的方法是将XML文件的内容文本写到一个字符串中,并将这个字符串存储到document.xml文件中,然后重新使用ZipOutStream类压缩所有的内容。另一个方法是使用一些可以编辑zip文件内容的第三方工具(或创建一个),但这些已经脱离了JDK的基本内容,所以在这篇文章中我们将使用ZipOutStream方法。
为了达到我们的目的,我们需要做很多事情。首先,Java应用程序必须定位到DOM的层次结构中,找到“t”节点,然后将它的文本内容替换为我们要写入到Word文档中的内容。(“Hello,Office 2007,from Java6!”是个不错的选择)产生的新DOM实例必须要保存到磁盘中,使用Java XML 应用程序接口时这并不是一个简单的任务。(简单的说来,开发者需要从javax.xml.transform包中创建一个Transformer,然后将XML转换为一个StreamResult,再交由ByteArrayOutputStream处理。)
一旦上面这些事情都处理完毕后,代码必须要产生一个ZIP格式的文件,是时候使用ZipOutputStream了,但由于只需要改变文档的内容,而不需要改变它的样式、字体以及格式,其他的部分可以从原始的文件中拷贝过来。使用一个简单的循环,遍历原始文件中的ZipEntries中所有的内容(除了word/document.xml,该文件中的内容需要被改变)并将其导出到一个新的ZipEntry中并写入该实体就足够了。当所有的工作都完成后,代码将会是以下的样子:
@Test public void modifyDocumentAndSave() throws IOException, ZipException, SAXException, ParserConfigurationException, TransformerException, TransformerConfigurationException { ZipFile docxFile = new ZipFile(new File("hello.docx")); ZipEntry documentXML = docxFile.getEntry("word/document.xml"); InputStream documentXMLIS = docxFile.getInputStream(documentXML); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); Document doc = dbf.newDocumentBuilder().parse(documentXMLIS); Element docElement = doc.getDocumentElement(); assertEquals("w:document", docElement.getTagName()); Element bodyElement = (Element) docElement.getElementsByTagName("w:body").item(0); assertEquals("w:body", bodyElement.getTagName()); Element pElement = (Element) bodyElement.getElementsByTagName("w:p").item(0); assertEquals("w:p", pElement.getTagName()); Element rElement = (Element) pElement.getElementsByTagName("w:r").item(0); assertEquals("w:r", rElement.getTagName()); Element tElement = (Element) rElement.getElementsByTagName("w:t").item(0); assertEquals("w:t", tElement.getTagName()); assertEquals("Hello, from Office 2007!", tElement.getTextContent()); tElement.setTextContent( "Hello, Office 2007, from Java6!"); Transformer t = TransformerFactory.newInstance().newTransformer(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); t.transform(new DOMSource(doc), new StreamResult(baos)); ZipOutputStream docxOutFile = new ZipOutputStream( new FileOutputStream("response.docx")); Enumeration entriesIter = docxFile.entries(); while (entriesIter.hasMoreElements()) { ZipEntry entry = entriesIter.nextElement(); if (entry.getName().equals("word/document.xml")) { byte[] data = baos.toByteArray(); docxOutFile.putNextEntry( new ZipEntry(entry.getName())); docxOutFile.write(data, 0, data.length); docxOutFile.closeEntry(); } else { InputStream incoming = docxFile.getInputStream(entry); byte[] data = new byte[1024 * 16]; int readCount = incoming.read(data, 0, data.length); docxOutFile.putNextEntry( new ZipEntry(entry.getName())); docxOutFile.write(data, 0, readCount); docxOutFile.closeEntry(); } } docxOutFile.close(); }
很抱歉这里展示了这么多代码,但是说实在的,这也是Java相比其他语言或者库的一个弱点。幸运的是我们的努力得到了以下的回报:
显然我们可以作很多事情来改善上面的场景。
首先,一个更好的XML操作库,可以更好的支持XPath技术,能够原生的序列化XML DOM结构到磁盘的库会对减少大量的代码有所帮助。JDOM,一个开源的Java/XML库(可以在jdom.org中找到),是一个可用的选择。Apache的XMLBeans也不错。一个必然的结果是我们可以获得更好的描述OpenXML格式的模式文档,并使用它们来产生一系列的Java类来更好的反映OpenXML文档的格式。开发者则可以更好的使用原生的Java类工作,而不是通过“Document”类和“Element”类。
其次,这些方法可以被绑定到一个更加针对Office的应用程序接口当中,可以改善针对实际存储的Word(或是Excel,PowerPoint)文档的XML文件操作的抽象层,关注那些拥有段落,字体等等其他的文档。实质上,像POI那样的库应该可以通过更新类反映Office XML格式的改动,理想的话,可以同时支持写入二进制结构化存储格式和新的OpenXML格式。
再次,Java可以对其ZIP文件格式的支持进行一些改动,同样,这样的目的也可以由使用一些第三方的库来完成。
尽管使用了一些笨重的应用程序接口调用,但是当想到Office平台对Java开发人员有多开放时还是非常的令人激动和振奋。在Java和Office应用程序的互操作性上,在Java应用程序中使用Office,还有在Java中创建和读写Office文件格式上,Office平台对Java社区的开发人员比以往任何时候都更加开放了。
本文附带的示例代码可以在此处下载。
Java无需解压直接读取Zip文件和文件内容
简单例子:
package test;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
public class aaaa {
public static void main(String[] args) throws Exception {
try {
readZipFile("D:\\ztree.zip");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void readZipFile(String file) throws Exception {
ZipFile zf = new ZipFile(file);
InputStream in = new BufferedInputStream(new
FileInputStream(file));
ZipInputStream zin = new ZipInputStream(in);
ZipEntry ze;
while ((ze = zin.getNextEntry()) != null) {
if (ze.isDirectory()) {
} else {
System.err.println("file - " + ze.getName() + " : "
+ ze.getSize() + " bytes");
long size = ze.getSize();
if (size > 0) {
BufferedReader br = new BufferedReader(
new InputStreamReader
(zf.getInputStream(ze)));
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
}
System.out.println();
}
}
zin.closeEntry();
}
}
POI解决word2007中字符替换问题
首先就是要明白解决的方案是:先删后添。就是利用Paragraph创建run,在创建完毕后循环遍历tableàrowàcell,获得到cell里的值,即cell.getText();在这里注意cell中value的长度,建议打出来看看已在后面于替换后的cell的值做对比。同时我们应该多多关注底层文件和源码,这样可以更加方便的了解他的底层构造。可以将word的后缀名改为zip然后打开找到word/document.xml文件,查看构造尤其是表的结构(比如说多关注关注
<w:tc>
<w:tcPr>
<w:tcW w:w="852" w:type="dxa" />
</w:tcPr>
<w:p w:rsidR="00B2466A" w:rsidRDefault="004E26BC">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia" />
</w:rPr>
<w:t>02</w:t>
</w:r>
</w:p>
</w:tc>
)这样有助于思维的扩展;
(现在d盘建立一个word,里面要有表格的哦,切记word文档是2007版的,如果2003版可以使用range对象就能轻松解决哦,网上很多的)
package test;
import java.io.FileOutputStream;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
try {
OPCPackage pack = POIXMLDocument.openPackage("d:/c.docx");
XWPFDocument doc = new XWPFDocument(pack);
Iterator<XWPFTable> it = doc.getTablesIterator();
while(it.hasNext()){
XWPFTable table = it.next();
List<XWPFTableRow> rows=table.getRows();
for(XWPFTableRow row : rows){
List<XWPFTableCell> cells = row.getTableCells();
for(XWPFTableCell cell: cells){
/*System.out.println(cell.getText());
System.out.println(cell.getText().length()+":length");*/
// System.out.println(cell.addParagraph().getText().length()+":--------------");//xx
// 获得传过来的字符串的长度
//这里是判断替换的条件,
if(cell.getText().equals("00")){
//关键的一步,删掉原有的段落
cell.removeParagraph(0);
//给段落重新赋值,不用担心要重新创建段落,他会自动创建的
cell.setText("爱爱");
/* System.out.println(cell.getText().length()+"====================");
*/
}
//在这里,主要问题不是判断的问题。而是在判断后如何把原有的之用心之替换掉的问题,
/* if(cell.getText().equals("00")){
System.out.println();
cell.setText("啊啊");
// System.out.println(cell.getText().length()+"====================");
} */
//===========================段落可以不要
List<XWPFParagraph> pars = cell.getParagraphs();
for(XWPFParagraph par:pars){
List<XWPFRun> runs = par.getRuns();
for(XWPFRun run:runs){
run.removeBreak();
}
/* System.out.println(par.getText());
System.out.println(par.getFootnoteText()+"NO1");
System.out.println(par.getParagraphText()+"NO2");
System.out.println(par.getPictureText()+"NO3");
System.out.println(par.getStyle()+"NO4");
System.out.println(par.getStyleID()+"NO5");
System.out.println(par.getAlignment()+"NO6");
System.out.println(par.getCTP()+"NO7");
System.out.println(par.getDocument()+"NO8");
System.out.println(par.getElementType()+"NO9");*/
}
}
}
}
FileOutputStream fos = new FileOutputStream("d:/bbb.docx");
doc.write(fos);
fos.flush();
fos.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}