Java中根据模板导出数据到word的解决方案我们需求如下给你一个模板里面有一个表格标题已经给好程序主要就是完成把数据填写到word中并提供给用户下载
网上找了很久发现主要两种开源包POI和JacobJacob首先被否决掉了因为他最后必须运行在windows平台上Excel导入导出我就是用的POI但是POI中的word操作实在不行读取还可以写入数据远不能满足项目的需要后面尝试诸如生成PDF然后转Word生成XML转Word生成Html转word生成rtf转word这些都是可行的办法但是网上开源包的功能有限iText对PDF操作是很强但是对PDF内容的解析不行
最后这个事情拖了四天我看文档的时候想到mht文件于是我把Word模板导出成mht文件然后查看其源码其实就是html代码我想这样可以用velocity填写数据然后直接导出成doc格式的文档后来这样基本满足我们项目的需要
#foreach($bean in $beanList)
<tr style=Dmsoyftiirow:;msoyftilastrow:yes;pagebreakinside:avoid;
height:pt>
<td width=D% style=Dwidth:%;border:solid windowtext pt;borde=
rtop:
none;msobordertopalt:solid windowtext pt;msoborderalt:solid window=
text pt;
padding:cm pt cm pt;height:pt>
<p class=DMsoNormal align=Dcenter style=Dtextalign:center><span style=Dmsobidifontsize:pt;fontfamily:SimSun>${beanseqNo}<o:p></o:p></span=
></p>
</td>
<td width=D% style=Dwidth:%;bordertop:none;borderleft:none;
borderbottom:solid windowtext pt;borderright:solid windowtext pt;
msobordertopalt:solid windowtext pt;msoborderleftalt:solid window=
text pt;
msoborderalt:solid windowtext pt;padding:cm pt cm pt;height:=
pt>
<p class=DMsoNormal align=Dcenter style=Dtextalign:center><span
style=Dmsobidifontsize:pt;fontfamily:SimSun>${beanname}<span
lang=DENUS><o:p></o:p></span></span></p>
</td>
</tr>
#end
上面是用mht源码改写成vm文件的部分代码就和生成一般的文本文件一摸一样这儿如果直接把汉字特殊字符什么的传给模板(当然英文不会)会出现乱码导致根据模板导出的word文件这个都是乱码关于乱码的详细解释可以参考 也就是说导出时需要把带汉字的字符串都进行转码这儿给出一些方法
/**
* 把给定的str转换为进制表示的unicode格式为姨
* 目前只是用于mht模板的转码
* @param str
* @return
*/
public static String encodeHtmlUnicode(String str) {
if(str == null) return ;
StringBuilder sb = new StringBuilder(strlength() * );
for (int i = ; i < strlength(); i++) {
sbappend(encodeHtmlUnicode(strcharAt(i)));
}
return sbtoString();
}
public static String encodeHtmlUnicode(char character) {
if (character > ) {
return &# + (character & xffff) + ;;
} else {
return StringvalueOf(character);
}
}
public static String encodeHtmlUnicode(Character character) {
if(character == null) return null;
return encodeHtmlUnicode(charactercharValue());
}
public static void encodeHtmlUnicode(String[] value) {
if(value == null || valuelength < ) return;
for(int i = ; i < valuelength; i ++) {
value[i] = encodeHtmlUnicode(value[i]);
}
}
上面的这些方法在项目中可以通过递归来对bean进行转码当然我在项目中只是针对String和Character两种数据类型进行转码
static Object encodeStringAndCharacter(Object value) {
if(value instanceof String) {
return StringUtilsreplaceNullString(EncodeUtilsencodeHtmlUnicode((String) value));
} else if(value instanceof Character) {
return StringUtilsreplaceNullString(EncodeUtilsencodeHtmlUnicode((Character) value));
}
return null;
}
static void encodeExceptStringAndCharacter(Object value) throws Exception {
if(value instanceof String[]) {
EncodeUtilsencodeHtmlUnicode((String[]) value);
} else if(value instanceof List) {
encodeHtmlUnicode((List) value);
} else if(value instanceof Form) {
encodeHtmlUnicode((Form) value);
} else if(value instanceof Form[]) {
encodeHtmlUnicode((Form[]) value);
}
}
@SuppressWarnings(unchecked)
static void encodeHtmlUnicode(List value) throws Exception {
if(value == null || valuesize() < ) return;
for(int i = ; i < valuesize(); i ++) {
Object ele = valueget(i);
Object result = encodeStringAndCharacter(ele);
if(result != null) {
valueset(i result);
} else {
encodeExceptStringAndCharacter(ele);
}
}
}
static void encodeHtmlUnicode(Form[] value) throws Exception {
if(value == null) return;
for(int i = ; i < valuelength; i ++) {
encodeHtmlUnicode(value[i]);
}
}
static void encodeHtmlUnicode(Form value) throws Exception {
if(value == null) return;
PropertyDescriptor[] pds = PropertyUtilsgetPropertyDescriptors(valuegetClass());
if(pds == null) return;
for(int i = ; i < pdslength; i ++) {
PropertyDescriptor pd = pds[i];
Object fieldValue = PropertyUtilsgetProperty(value pdgetName());
Class fieldType = pdgetPropertyType();
if(StringclassisAssignableFrom(fieldType)) {
PropertyUtilssetProperty(value
pdgetName()
StringUtilsreplaceNullString(EncodeUtilsencodeHtmlUnicode((String) fieldValue)));
} else if(ListclassisAssignableFrom(fieldType)) {
encodeHtmlUnicode((List) fieldValue);
} else if(FormclassisAssignableFrom(fieldType)) {
encodeHtmlUnicode((Form) fieldValue);
} else if(Form[]classisAssignableFrom(fieldType)) {
encodeHtmlUnicode((Form[]) fieldValue);
}
}
}
上面的Form类型不需要关心就是一个Bean接口这样在传到模板之前进行上述转码就基本上可以避免乱码问题
导出结束后只需要把OutputStream导出到*doc文件中即可下载的话只要设置
responsesetHeader(Contentdisposition attachment; filename= + URLEncoderencode(***doc DEFAULT_WEB_ENCODING));
项目还有一个需求对于一些飞机零件或者领导名字要求是图片显示这个不难但模板做起比较繁琐因为mht模板和难控制一般都是通过word另存的而且里面汉字有时ascii格式所以需要显示图片的地方都事先放一个图片然后再在mht代码中替换一下
#if($manager)
<![if gte vml =
]><v:shape
id=D_x_i type=D#_x_t style=Dwidth:pt;height:pt>
<v:imagedata src=Dfilefiles/imagegif o:title=D/>
</v:shape><![endif]><![if !vml]><img width=D height=D
src=Dfilefiles/imagegif v:shapes=D_x_i><![endif]>
#end
#if($manager)
=_NextPart_CEBAECC
ContentLocation: file:///C:/CD/filefiles/imagegif
ContentTransferEncoding: base
ContentType: image/gif
${manager}
#end
在mht文件中图片都是以base码来存储的所以在存取之前必须把二进制的图片进行base编码
static BASEEncoder baseEncoder = new BASEEncoder();
/**
* 把给定的二进制流变成base码
* @param in
* @return
* @throws IOException
*/
public static synchronized String baseEncode(InputStream in) throws IOException {
byte[] img = new byte[inavailable()];
inread(img);
return baseEncode(img);
}
public static synchronized String baseEncode(String in) throws IOException {
return baseEncode(ingetBytes());
}
public static synchronized String baseEncode(byte[] in) throws IOException {
return baseEncoderencode(in);
}
转换成String以后在传到模板文件中显示则可关于任何格式布局等等等的调整你可以事先在word里先全部调整好再另存导出效果如下图
上面的编号表格数据横线上的都是填写的
这种导出到word的方法的一个缺点是模板复杂繁琐尤其修改的时候更是如此而且必须是officexp及其以上版本