前言
作者本周正在开发一个报告导出服务,但是遇到了一个奇葩问题卡了近一天的时间,发现了Jacob的不少被忽略的问题,在这里统一记录一下。
Jacob简介
不少人以为Jacob是一个报告生成的库,但实际上jacob并不是一个报告生成的库。,JACOB is a JAVA-COM Bridge that allows you to call COM Automation components from Java. It uses JNI to make native calls to the COM libraries. 换成人话就是,通过JAVA的JNI技术来调用COM组件罢了,可以调用Word可以调用Excel也可以调用其他基于COM组件技术的软件。可以这么理解,他是一个在JAVA和COM组件之间的桥梁,JAVA以JNI的方式通过Dispatch ,Variant等接口与COM组件进行通讯。
主要问题
基本原理
记住下面这句话即可:
Jacob通过java的JNI调用COM组件的接口。
由于Java的JNI是通过Native的方式调用的COM组件的,必须遵守COM的接口规则。所以就有了如下的奇葩的用法:
Dispatch selection = Dispatch.get(wordApp, "Selection").toDispatch();
Dispatch inLineShapes = Dispatch.get(selection, "InLineShapes").toDispatch();
Dispatch picture = Dispatch.call(inLineShapes, "AddPicture", imgPath).toDispatch();
//选中图片
Dispatch.call(picture, "Select");
//设置宽度高度
Dispatch.put(picture, "Width", new Variant(10));
Dispatch.put(picture, "Height", new Variant(10));
第一次看到这种API绝对是超出了我的认知范围,Dispatch是啥,为啥都是Dispatch,调个方法为啥还要call。别急基本概念如下所示:
ComThread:com组件管理,用来初始化com线程,释放线程,所以会在操作office之前使用,操作完成再使用。
ActiveXComponent:创建office的一个应用,比如你操作的是word还是excel
Dispatch:调度处理类,封装了一些操作来操作office,里面所有的可操作对象基本都是这种类型,所以jacob是一种链式操作模式,就像StringBuilder对象,调用append()方法之后返回的还是StringBuilder对象
Variant:封装参数数据类型,因为操作office是的一些方法参数,可能是字符串类型,可能是数字类型,虽然都是1,但是不能通过,可以通过Variant来进行转换通用的参数类型,new
Variant(1),new Variant(“1”), Dispatch的几种静态方法:这些方法就是要用来操作office的。
•call( )方法:调用COM对象的方法,返回Variant类型值。 •invoke( )方法:和call方法作用相同,但是不返回值。
•get( )方法:获取COM对象属性,返回variant类型值。 •put( )方法:设置COM对象属性。
以上方法中有的有很多重载方法,调用不同的方法时需要放置不同的参数,至于哪些参数代表什么意思,具体放什么值,就需要参考vba代码了,仅靠jacob是无法进行变成的。
Variant对象的toDispatch()方法:将以上方法返回的Variant类型转换为Dispatch,进行下一次链式操作。
上面引自博客。
兼容性问题
由于是通过Java的JNI调用COM组件实现的控制Word所以只能兼容windows系统,其他系统的朋友可以洗洗睡了,简单的功能可以通过POI来实现。
经过测试Jacob对于Word的版本的兼容性还是不错的,从Word 2010到Office 365的Word都可以。
32位和64位的互不兼容,一定要根据机器来选择合适的版本,放到Java的bin目录下。
要保证机器,Jacob的jar以及dll三者的版本是统一的。
Jacob封装
网上关于jacob的工具类都是千篇一律,感觉是一个来源粘来粘去的。不过实际上生成报告的用到的功能就那几个。
这里只给出典型的替换API的实现思路:
要想直接通过后台从无到有生成一个报告并不是一个简单的事,但基于一个已知的模板加上合适的替换就简单多了,而且可以满足80%的需求。替换文本的基本思路,是事先在模板里定义好要替换内容的占位符,再通过代码进行动态替换即可,占位符我习惯用下面的形式${abc}$。这样可以直接替换文本,结果可以带有本来的格式,我们的程序就可以不用关心格式问题,大大简化了业务复杂度。
具体的封装可以参考一下几个例子:
扩展Jacob工具类的方法
如果需要扩展封装的工具类,添加自己需要的功能,可以参考以下资料:
微软官网API链接,找到这个参考真的是不容易,有用的参考资料真的是少的可怜,根据网上的蛛丝马迹找到的这个参考地址,这个参考资料可以说异常强大,有了这个几乎拥有了全部的word的能力。
遇到的坑
1.文档打开失败
可以尝试手动打开word程序在试试。
2.插入文本后,总会导致内容莫名的消失
采用TypeParagraph或是创建表格后总是导致某些内容消失,这其实是于Word本身的 选中状态有关的,如果本身选中了某些东西在执行插入指令会导致选中内容的消失,可以采用InsertParagraphBefore或InsertParagraphAfter来解决这个问题。
3.想要获得插入的表格是第几个表格
这个问题折磨了一整天,最后也没搞定,没办法曲线救国,不再纠结于第几个表,而是采用通过表格占位符的方式创建表格的方式来定位和插入表格,在拥有表格对象时就直接创建其内容,而且把表格标题最后统一处理。有哪位大哥找到方法,别忘了告诉我一下。
总结
本周在解决Jacob相关问题的时候,只是想找一个获取表格序号的方法,只知道是用的Jacob,于是满世界查找Jacob API,结果一无所获,从官网路过无数次却没有看到一个跟word有关的字眼,感觉一定是上了假官网,百度不行就上google,结果还是毫无进展。
结果就在一个页面上看到了一个人说要插入一个空行,用了一个我没见过的TypeParagraph方法,这才打开了一扇新的大门。
如果你遇到了一个本来早就应该有人解决的问题,但迟迟在网上找不到答案,或其他人讨论的痕迹,那么可以查看一下思考的方向或是先前的假设存在问题。