最近公司的项目要求实现word文档根据模板导出和打印。
我采用Jacob的来实现。
Tomcat服务启动后,第一次导出和打印,没有异常。再次调用时,报出异常:
需要重新启动服务。
word操作的实现类:
解决:
经过多次调试,发现内存消耗严重。查看任务管理器,有多个word进程积压未关闭。
发现原因:第一次调用完后,虽然word已经被关闭。但装载word的ActiveXComponent并没有被关闭。
所以,加入管理com组件的线程,在关闭word之后,关闭线程。
重构后的代码:
我采用Jacob的来实现。
Tomcat服务启动后,第一次导出和打印,没有异常。再次调用时,报出异常:
操作word文档失败!com.jacob.com.ComFailException: Can't map name to dispid: Open
2009-03-17 15:11:41,812 WARN [org.apache.struts.action.RequestProcessor] - <Unhandled Exception thrown: class com.jacob.com.ComFailException>
2009-3-17 15:11:41 org.apache.catalina.core.StandardWrapperValve invoke
严重: Servlet.service() for servlet action threw exception
com.jacob.com.ComFailException: Can't map name to dispid: Quit
需要重新启动服务。
word操作的实现类:
public class JacobWord {
private boolean saveOnExit;
//word文档
Dispatch doc = null;
//word运行程序对象
static ActiveXComponent word;
//所有word文档
static Dispatch documents;
static{
word = new ActiveXComponent("Word.Application");
word.setProperty("Visible", new Variant(false));
documents = word.getProperty("Documents").toDispatch();
}
//构造函数
public JacobWord(){
saveOnExit = true;
}
/**
* 打开文件
* @param inputDoc 要打开的文件,全路径
* @return 打开的文件
*/
public Dispatch open(String inputDoc){
return Dispatch.call(documents, "Open", inputDoc).toDispatch();
}
/**
* 选定内容
* @return 选定的范围或插入点
*/
public Dispatch select(){
return word.getProperty("Selection").toDispatch();
}
/**
* 把插入点移动到文件首位置
* @param selection 插入点
*/
public void moveStart(Dispatch selection){
Dispatch.call(selection, "HomeKey", new Variant(6));
}
/**
* 从选定内容或插入点开始查找文本
* @param selection 选定内容
* @param toFindText 要查找的文本
* @return true:查找到并选中该文本;false:未查找到文本。
*/
public boolean find(Dispatch selection,String toFindText){
//从selection所在位置开始查询
Dispatch find = Dispatch.call(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(Dispatch selection,String newText){
Dispatch.put(selection, "Text", newText);
}
/**
* 全局替换
* @param selection 选定内容或起始插入点
* @param oldText 要替换的文本
* @param replaceObj 替换为文本
*/
public void replaceAll(Dispatch selection,String oldText,Object replaceObj){
//移动到文件开头
moveStart(selection);
String newText = (String)replaceObj;
while(find(selection,oldText)){
replace(selection,newText);
Dispatch.call(selection, "MoveRight");
}
}
/**
* 保存文件
* @param outputPath 输出文件(包含路径)
*/
public void save(String outputPath){
Dispatch.call(Dispatch.call(word, "WordBasic").getDispatch(), "FileSaveAs",outputPath);
}
/**
* 关闭文件
* @param doc 要关闭的文件
*/
public void close(Dispatch doc){
Dispatch.call(doc, "Close",new Variant(saveOnExit));
}
/**
* 根据模板、数据生成word文件
* @param inputPath 模板文件(包含路径)
* @param outPath 输出文件(包含路径)
* @param data 数据包(包含要填充的字段、对应的数据)
*/
public void toWord(String inputPath,String outPath,HashMap data){
String oldText;
Object newValue;
try{
doc = open(inputPath);
Dispatch selection = select();
Iterator keys = data.keySet().iterator();
while(keys.hasNext()){
oldText = (String)keys.next();
newValue = data.get(oldText);
replaceAll(selection,oldText,newValue);
}
save(outPath);
}catch(Exception ex){
System.out.println("操作word文档失败!"+ex);
}finally{
if(doc != null){
close(doc);
}
}
}
public void saveDoc(String inPath,String outPath,HashMap map,boolean isPrint){
toWord(inPath,outPath,map);
ActiveXComponent objWord = new ActiveXComponent("word.Application");
Dispatch wordObject = (Dispatch)objWord.getObject();
Dispatch.put((Dispatch)wordObject,"Visible",new Variant(false));
Dispatch documents = objWord.getProperty("Documents").toDispatch();
Dispatch document = Dispatch.call(documents, "Open", outPath).toDispatch();
if(isPrint){
Dispatch.call(document, "PrintOut"); //打印
}
Dispatch.call(document, "Close", new Variant(false));
objWord.invoke("Quit", new Variant[0]);
word.invoke("Quit", new Variant[0]);
}
}
解决:
经过多次调试,发现内存消耗严重。查看任务管理器,有多个word进程积压未关闭。
发现原因:第一次调用完后,虽然word已经被关闭。但装载word的ActiveXComponent并没有被关闭。
所以,加入管理com组件的线程,在关闭word之后,关闭线程。
重构后的代码:
public class JacobWord {
/**
* 打开文件
* @param documents
* @param inputDocPath
* @return
*/
private Dispatch open(Dispatch documents,String inputDocPath){
return Dispatch.call(documents,"Open",inputDocPath).toDispatch();
}
/**
* 选定内容
* @param word
* @return
*/
private Dispatch select(ActiveXComponent word){
return word.getProperty("Selection").toDispatch();
}
/**
* 把插入点移动到文件首位置
* @param selection
*/
private void moveStart(Dispatch selection){
Dispatch.call(selection, "HomeKey",new Variant(6));
}
/**
* 从选定内容或插入点开始查找文本
* @param selection 选定内容
* @param toFindText 要查找的文本
* @return true:查找到并选中该文本;false:未查找到文本。
*/
private boolean find(Dispatch selection,String toFindText){
//从selection所在位置开始查询
Dispatch find = Dispatch.call(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
*/
private void replace(Dispatch selection,String newText){
Dispatch.put(selection, "Text", newText);
}
/**
* 全局替换
* @param selection
* @param oldText
* @param replaceObj
*/
private void replaceAll(Dispatch selection,String oldText,Object replaceObj){
moveStart(selection);
String newText = (String)replaceObj;
while(find(selection,oldText)){
replace(selection,newText);
Dispatch.call(selection, "MoveRight");
}
}
/**
* 打印
* @param document
*/
private void print(Dispatch document){
Dispatch.call(document, "PrintOut");
}
/**
* 保存文件
* @param word
* @param outputPath
*/
private void save(ActiveXComponent word,String outputPath){
Dispatch.call(Dispatch.call(word, "WordBasic").getDispatch(), "FileSaveAs",outputPath);
}
/**
* 关闭文件
* @param doc
*/
private void close(Dispatch doc){
Dispatch.call(doc, "Close",new Variant(true));
}
/**
* 保存打印doc文档
* @param inputDocPath
* @param outPutDocPath
* @param data
* @param isPrint
*/
public void saveDoc(String inputDocPath,String outPutDocPath,HashMap data,boolean isPrint){
//初始化com的线程
ComThread.InitSTA();
//word运行程序对象
ActiveXComponent word = new ActiveXComponent("Word.Application");
//文档对象
Dispatch wordObject = (Dispatch) word.getObject();
//设置属性 Variant(true)表示word应用程序可见
Dispatch.put((Dispatch)wordObject,"Visible", new Variant(false));
//word所有文档
Dispatch documents = word.getProperty("Documents").toDispatch();
//打开文档
Dispatch document = this.open(documents,inputDocPath);
Dispatch selection = this.select(word);
Iterator keys = data.keySet().iterator();
String oldText;
Object newValue;
while(keys.hasNext()){
oldText = (String)keys.next();
newValue = data.get(oldText);
this.replaceAll(selection, oldText, newValue);
}
//是否打印
if(isPrint){
this.print(document);
}
this.save(word,outPutDocPath);
this.close(document);
word.invoke("Quit", new Variant[0]);
//关闭com的线程
ComThread.Release();
}
}