使用freemarker转word简单教程
事前准备
由于ftl 文件可能在idea 中报 约束未注册的问题 URI is not registered 请参考下面的博客
链接: idea中xml或者ftl文件xmlns约束报红解决方案.
由于ftl 文件可能在eclipse中缺少freemarker 编译器导致失败的问题 要安装 freemarker ide 请参考下面的博客
链接: eclipse中离线安装 freemarker IDE 插件和jad反编译插件.
编辑ftl文件时常用的freemarker语法 请参考下面的博客
链接: freemarker ftl文件简单语法整理.
下载jar包
freemarker-2.3.30.jar
博客这两天上传附件老是失败,就不放在博客里了,有需要可以给我留言,或者自行搜索下载
编辑一个word模板
如图所示,一些要传参的地方可以提前使用freemarker的ftl语法,加入变量
模板处理
将编辑好的word转存为xml 格式,使用notepad++ 或者 ue打开
这是代码都是一行的比较乱,使用idea中 Ctrl+Alt+L 进行格式化,
或者 notepad++中的 插件 -> XML Tools-> pretty print(XML only with line breaks),或者直接快捷键 Ctrl alt shift B,进行格式化
编译之后每一行的标签是<w:tr>
同时注意对于列表中
要使用
表格循环用标签
<#list userList as user>
<w:tr>
姓名:{user.sex}
</w:tr>
</#list>
再就是如果有图片会看到图片占位的地方,有一片base64编码后的代码,把base64替换成${image}
<pkg:part pkg:name="/word/media/image1.jpg" pkg:contentType="image/jpeg" pkg:compression="store">
<pkg:binaryData>${imagej}</pkg:binaryData>
</pkg:part>
<pkg:part pkg:name="/word/media/image2.PNG" pkg:contentType="image/png" pkg:compression="store">
<pkg:binaryData>${imagep}</pkg:binaryData>
</pkg:part>
在此过程中可以将创建人,文件生成时间等一些属性删去也是没有问题的
最后,更改文件后缀为 。.ftl
代码实现
工具处理类
WordUtil
package com.yucheng.cmis.util;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.Version;
import sun.misc.BASE64Encoder;
public class WordUtil {
// FreeMarker 配置
private static Configuration cfg;
//默认的给定公共文件路径
private static String basePath = System.getProperty("user.dir")+"/WebContent/WEB-INF/template";
/**
* @author JL-J 静态内部类,创建freeMarker 配置,实例化工具类
*/
private static class LazyHolder {
// 创建 freeMarker 配置 ,2.3.30 引入的freemarker 版本号
private static final Configuration config = new Configuration(new Version("2.3.30"));
// 实例化工具类
private static final WordUtil wu = new WordUtil();
}
/**
* 私有构造函数
*/
private WordUtil() {
}
/**
* 初始化配置文件,获取实例
*
* @param templatePath 模板路径
* @return FreeMarkerUtil 工具类
*/
public static WordUtil getInstance() {
if (null == cfg) {
// 创建 freeMarker 配置
cfg = LazyHolder.config;
// 设置编码格式
cfg.setDefaultEncoding("UTF-8");
// templatePath 指的是模板所在路径
}
// 这是旧方式
// TemplateLoader templateLoader;
// try {
// templateLoader = new FileTemplateLoader(new File(basePath));
//
// cfg.setTemplateLoader(templateLoader);
// } catch (IOException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
try {
//使用文件路径加载模板
cfg.setDirectoryForTemplateLoading(new File(basePath));
} catch (IOException e) {
e.printStackTrace();
}
//使用类路径在载模板
//cfg.setClassForTemplateLoading(WordUtil.class, basePath);
// 设置异常处理器,这样的话就可以${as.df}, 即使没有对应属性也不会出错
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
return LazyHolder.wu;
}
/**
* 根据模版名称加载对应模版
*
* @param templateName 模版名称
* @return
*/
private Template getTemplate(String templateName) {
try {
return cfg.getTemplate(templateName);
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 控制台打印通过模板生成的文件
*
* @param dataModel 数据模型
* @param templateName 输出模版
*/
public String getContent(Map<String, Object> dataModel, String templateName) {
try {
StringWriter stringWriter = new StringWriter();
getTemplate(templateName).process(dataModel, stringWriter);
stringWriter.flush();
String result = stringWriter.toString();
stringWriter.close();
return result;
}
catch (TemplateException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 创建通过模板生成的文件
*
* @param dataModel 数据模型
* @param templateName 输出模版
* @param filePath 输出文件路径 "/WebContent/WEB-INF/template" 下 /word/test.doc
*/
public File create(Map<String, Object> dataModel, String templateName, String filePath) {
try {
filePath = basePath + filePath;
File file = new File(filePath);
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
BufferedWriter bufWrite = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath),"UTF-8"));
getTemplate(templateName).process(dataModel, bufWrite);
bufWrite.flush();
bufWrite.close();
return file;
}
catch (TemplateException e) {
e.printStackTrace();
}
catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 根据地址获得数据的字节流
*
* @param strUrl 网络连接地址
* @return 图片Base64码
*/
public String getImgBase64ByUrl(String strUrl) {
try {
// 建立 Http 链接
HttpURLConnection conn = (HttpURLConnection) new URL(strUrl).openConnection();
// 5秒响应超时
conn.setConnectTimeout(5 * 1000);
conn.setDoInput(true);
// 判断http请求是否正常响应请求数据,如果正常获取图片 Base64 码
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
// 获取图片输入流
InputStream inStream = conn.getInputStream();
// 用于存储图片输出流
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
// 定义缓存流,用于存储图片输出流
byte[] buffer = new byte[1024];
int len = 0;
// 图片输出流循环写入
while ((len = inStream.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
// 图片输出流转字节流
byte[] btImg = outStream.toByteArray();
inStream.close();
outStream.flush();
outStream.close();
return new String(Base64.encode(btImg));
}
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
//根据绝对路径获得图片的base64码
public static String getImageBase(String src) throws Exception {
if (src == null || src == "") {
return "";
}
File file = new File(src);
if (!file.exists()) {
return "";
}
InputStream in = null;
byte[] data = null;
try {
in = new FileInputStream(file);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
}
package com.yucheng.cmis.util;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WordTest {
public static void main(String[] args) {
String fileName = "/word/wer/test.doc";
String ftlName = "wer/teset.ftl";
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("title","这就是我填的标题");
List<Map<String,Object>> list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "何时何地是");
map.put("age", 234);
map.put("address", "暗杀第三方公司的规划");
map.put("cd", "爱上对方");
map.put("df", "afgsdg");
list.add(map);
}
dataMap.put("list",list);
dataMap.put("time","2020-04-19");
try {
dataMap.put("imagej",WordUtil.getImageBase("D:/用户目录/Downloads/博客附件/24.jpg"));
dataMap.put("imagep",WordUtil.getImageBase("D:/用户目录/Downloads/博客附件/2.PNG"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
WordUtil util = WordUtil.getInstance();
File file = util.create(dataMap, ftlName, fileName);
System.out.println(file.getName());
}
}
//如果是要下载的话 使用 servlet 中的request 和 response
根据上面的代码最后拿到 File 之后,对file进行IO流的下载
InputStream inputStream = null;
ServletOutputStream out = null;
try {
File file = util.create(dataMap, ftlName, fileName);
inputStream = new FileInputStream(file);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/msword");
response.addHeader("Content-Disposition", "attachment;filename "+file.getName()+");
out = response.getOutputStream();
byte[] buffer = new byte[1024]; // 缓冲区
int bytesToRead = -1;
// 通过循环将读入的Word文件的内容输出到浏览器中
while ((bytesToRead = inputStream.read(buffer)) != -1) {
out.write(buffer, 0, bytesToRead);
}
} catch (Exception e) {
// TODO: handle exception
} finally {
if (inputStream != null)
inputStream.close();
if (out != null)
out.close();
if (file != null)
file.delete();
}
}