使用freemarker生成word,步骤详解并奉上源代码
1、 步骤
1、 用word编辑好模板
1、 普通字符串替换为 ${string}
2、 表格循环用标签
<#list userList as user>
姓名:${user.userName} , 性别:${user.sex}
</#list>
word模板原型如下图:
最终生成的结果如下:
2、 将word模板另存为xml格式
选中 [ 仅保存数据 ]
选择 [ 保持wordML(K) ]
3、 将xml模板文件后缀名改为.ftl
4、 编辑ftl文件
搜索关键字,补入 <#list userList as user> </#list>或其它freemarker标签
如下图:
2、 Java代码
package com.lun.utils;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.Map;
import freemarker.template.Configuration;
import freemarker.template.Template;
/**
* @Desc:word操作工具类
* @Author:张轮
* @Date:2014-1-22下午05:03:19
*/
public class WordUtil {
/**
* @Desc:生成word文件
* @Author:张轮
* @Date:2014-1-22下午05:33:42
* @param dataMap word中需要展示的动态数据,用map集合来保存
* @param templateName word模板名称,例如:test.ftl
* @param filePath 文件生成的目标路径,例如:D:/wordFile/
* @param fileName 生成的文件名称,例如:test.doc
*/
@SuppressWarnings("unchecked")
public static void createWord(Map dataMap,String templateName,String filePath,String fileName){
try {
//创建配置实例
Configuration configuration = new Configuration();
//设置编码
configuration.setDefaultEncoding("UTF-8");
//ftl模板文件统一放至 com.lun.template 包下面
configuration.setClassForTemplateLoading(WordUtil.class,"/com/lun/template/");
//获取模板
Template template = configuration.getTemplate(templateName);
//输出文件
File outFile = new File(filePath+File.separator+fileName);
//如果输出目标文件夹不存在,则创建
if (!outFile.getParentFile().exists()){
outFile.getParentFile().mkdirs();
}
//将模板和数据模型合并生成文件
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
//生成文件
template.process(dataMap, out);
//关闭流
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.lun.action;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.struts2.ServletActionContext;
import com.lun.utils.WordUtil;
import com.opensymphony.xwork2.ActionSupport;
/**
* @Desc:生成word
* @Author:张轮
* @Date:2014-1-22下午04:52:03
*/
@SuppressWarnings("serial")
public class WordAction extends ActionSupport{
private String filePath; //文件路径
private String fileName; //文件名称
private String fileOnlyName; //文件唯一名称
/**
* @Desc:生成word文档
* @Author:张轮
* @Date:2014-1-22下午07:29:58
* @return
*/
public String createWord() {
/** 用于组装word页面需要的数据 */
Map<String, Object> dataMap = new HashMap<String, Object>();
/** 组装数据 */
dataMap.put("userName","张三");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日");
dataMap.put("currDate",sdf.format(new Date()));
dataMap.put("content","这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容这是其它内容");
List<Map<String, Object>> newsList=new ArrayList<Map<String,Object>>();
for(int i=1;i<=10;i++){
Map<String, Object> map=new HashMap<String, Object>();
map.put("title", "标题"+i);
map.put("content", "内容"+(i*2));
map.put("author", "作者"+(i*3));
newsList.add(map);
}
dataMap.put("newsList",newsList);
/** 文件名称,唯一字符串 */
Random r=new Random();
SimpleDateFormat sdf1=new SimpleDateFormat("yyyyMMdd_HHmmss_SSS");
StringBuffer sb=new StringBuffer();
sb.append(sdf1.format(new Date()));
sb.append("_");
sb.append(r.nextInt(100));
//文件路径
filePath=ServletActionContext.getServletContext().getRealPath("/")+"upload";
//文件唯一名称
fileOnlyName = "用freemarker导出的Word文档_"+sb+".doc";
//文件名称
filename="用freemarker导出的Word文档.doc";
/** 生成word */
WordUtil.createWord(dataMap, "news.ftl", filePath, fileOnlyName);
return "createWordSuccess";
}
/**
* @Desc:下载生成的word文档,入口,用来跳转至struts XML配置
* @Author:张轮
* @Date:2014-1-22下午07:36:29
* @return
*/
public String dowloadWord() {
/** 先判断文件是否已生成 */
try {
//解决中文乱码
filePath = URLDecoder.decode(filePath, "UTF-8");
fileOnlyName = URLDecoder.decode(fileOnlyName, "UTF-8");
fileName = URLDecoder.decode(fileName, "UTF-8");
//如果文件不存在,则会跳入异常,然后可以进行异常处理
new FileInputStream(filePath + File.separator + fileOnlyName);
} catch (Exception e) {
e.printStackTrace();
return "error";
}
return "dowloadWord";
}
/**
* @Desc:下载生成的word文档
* 该方法是struts.xml文件中的: <param name="inputName">wordFile</param> 中自动对应的get方法,该方法自动调用
* @Author:张轮
* @Date:2014-1-22下午07:36:29
* @return 返回最终生成的word文档 文件流
*/
public InputStream getWordFile(){
try {
//解决中文乱码
fileName = URLDecoder.decode(fileName, "UTF-8");
/** 返回最终生成的word文件流 */
return new FileInputStream(filePath + File.separator + fileOnlyName);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileOnlyName() {
return fileOnlyName;
}
public void setFileOnlyName(String fileOnlyName) {
this.fileOnlyName = fileOnlyName;
}
}
struts2配置文件如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="word" namespace="/" extends="struts-default">
<action name="createWord" class="com.lun.action.WordAction" method="createWord">
<result name="createWordSuccess" type="redirectAction">
<param name="actionName">dowloadWord</param>
<param name="filePath">${filePath}</param>
<param name="fileOnlyName">${fileOnlyName}</param>
<param name="fileName">${fileName}</param>
<param name="encode">true</param>
</result>
</action>
<action name="dowloadWord" class="com.lun.action.WordAction" method="dowloadWord">
<result name="dowloadWord" type="stream">
<param name="contentDisposition">attachment;filename="${fileName}"</param>
<param name="inputName">wordFile</param>
<param name="encode">true</param>
</result>
<result name="error">/error.jsp</result>
</action>
</package>
</struts>
页面调用如下:
<input type="button" onclick="javascript:window.location.href='createWord.action'" value="生成Word"/>
3、 注意
1、 编辑word模板时,${string} 标签最好是手动一次性输入完毕,或者使用记事本统一将整个${string}编辑好之后,粘贴至word里边。
也就是说,不要在word里首先打完 ${ } 之后,又从其它地方把 string 字符串粘贴至 { } 之间,这样在 word 转化为 xml时,解析会有问题,freemarker解析时,会报错。
将${} 和 string 分开录入word,另存为xml后,后果将如下:
2、 如果word里有需要循环表格列,也就是横向循环表格时,建议尽量改为循环行,纵向循环,这样可以减少对 ftl 文件的 list标签插入,缩减工作量。