项目需要通过程序生成word文档,就是业务方提供了一份模板,里面的有一份表格或者合同,程序主要就是把数据填到word中,并提供用户下载。首先想到的是iText和POI。iText操作pdf还不错,但是对word貌似就很吃力了。POI对excel读取和操作还行,对word的写入太复杂。上网搜了下java导出word,发现可以通过FreeMarker来完成还能保持word中复杂的样式。我们的工程中已经使用了velocity,将freemarker换成velocity很轻松。这样的话导出简单方便,还支持复杂的样式。下次业务方要换模板的时候简单替换下就行了。
具体过程
1.将运营给的word文档,需要数据的地方用$变量符替换
测试用,这里就简单的设置了邮箱。
2.保存之后将word文档另存为xml格式。
3.将这个xml文件另存为.vm文件就是我们的模板了。
写个测试
public void execute(TurbineRunData rundata, Context context) throws IOException {
VelocityContext veContext = new VelocityContext();
veContext.put("email", "17173as");
// 渲染html
InputStreamReader reader = new InputStreamReader(this.getClass().getResourceAsStream("/green.vm"),"UTF-8");
String html = ExportUtil.buildHtmlByVelocity(veContext, reader);
String fileName="绿色通道.docx";
ServletOutputStream sos = null;
try {
HttpServletResponse response = rundata.getResponse();
response.setContentType("application/msword;");
response.setHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes(), "ISO-8859-1"));
sos = response.getOutputStream();
sos.print(html);
} catch (Exception e) {
log.warn("[导出除标的物异常]" + fileName, e);
} finally {
if (sos != null) {
try {
sos.close();
} catch (IOException e) {
log.error("[close异常]", e);
}
}
}
}
结果能导出,但是文件压根打不开,提示文件错误
将导出的doc文件用文本编辑器打开,初看挺正常的,之前的通过word导出的xml改为doc结尾后缀都能用word 再打开,这份重新导出的怎么就不行呢。用文本比较工具比较了下2份文件发现程序导出的文件存在一部分中文是?,应该就是这个引起的。但是为啥会部分是乱码,部分又是正常的中文。于是上网查了一通资料。中间换过好几种方法,一度想放弃xml,使用mth文件(html格式),但是悲剧的是word导出的mth打开就有乱码。于是又想到把xml文件转换成GBK编码,这样对中文总算友好吧。结果还不行,xml文件里居然有韩文之类的(不要问我为啥,我也不知道),GBK也行不通。那就查吧,看看是那一步导出乱码的,写了个main函数
public static void main(String[] args) throws IOException {
VelocityContext veContext = new VelocityContext();
veContext.put("email", "开");
// 渲染html
InputStreamReader reader = new InputStreamReader(ExportSettledGreenChannel.class.getResourceAsStream("/green.vm"),
"UTF-8");
int b;
while ((b = reader.read()) != -1)
{
System.out.print((char)b);
}
}
这个时候发现打印出来也是部分字是乱码,这时候尝试将green.vm用比较简单的中文替换原来的xml。发现能正常的打印出来,于是再换成word导出的xml内容,打印出来也是ok的,我还特意将打印出来的大段xml复制出来,改成doc后缀也能正常打开。这个就有点诡异了,当时觉得是我写的vm模板有问题,导致编码出错。现在ok了就重新打包测试一下。maven打包起tomcat,点击导出,发现还是跟之前一样的结果,还是打不开。这个就非常郁闷了,这时尝试将刚才的main函数再跑一下,发现居然也出乱码。看来是有八成跟打包有关系,因为我把vm放在resources目录下,这样可以通过class path加载。但是pom文件里配置的编码是GBK,而vm模板的编码是UTF-8,难道是maven编译的时候导致乱码。但是我不能改vm的编码也不能改pom里设置的编码。于是把vm文件放到WEB-INF下面,这样mavn打包不会对他产生影响。再来测试一下
public void execute(TurbineRunData rundata, Context context) throws IOException {
VelocityContext veContext = new VelocityContext();
veContext.put("email", "17173as");
InputStream greenInput= rundata.getRequestContext().getServletContext().getResourceAsStream("/WEB-INF/green.vm");
InputStreamReader reader = new InputStreamReader(greenInput,"UTF-8");
String html = ExportUtil.buildHtmlByVelocity(veContext, reader);
String fileName="绿色通道.doc";
ServletOutputStream sos = null;
try {
HttpServletResponse response = rundata.getResponse();
response.setContentType("application/msword;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + new String(fileName.getBytes(), "ISO-8859-1"));
sos = response.getOutputStream();
sos.print(html);
} catch (Exception e) {
log.warn("[导出除标的物异常]" + fileName, e);
} finally {
if (sos != null) {
try {
sos.close();
} catch (IOException e) {
log.error("[close异常]", e);
}
}
}
}
最后文件能正常的打开,而且email替换成了变量