思想说明:
本文内容的核心:使用FreeMarker的模板引擎技术,导出word。
总体步骤:
第一步:创建一个word模板,里面的对应位置使用FreeMarker的占位符表示。
注:为了导出的word兼容2003版本,我们创建的word模板,最好是doc后缀的。
注:本人用的是wps,office应该也是一样的(这个没试过)。
第二步:将该word,另存为xml文件。
提示:也可以第二步时先用一个特别的字符占位,然后在第三步时,再在xml中将对应的字符替换为FreeMarker能识别
的占位符。
第三步:打开该xml文件,检查占位符是否"变形"
提示:本人是以安装了indent XML插件的sublime打开的。
注:有时,另存为xml时,占位符可能会变形(如:“${age}”变为“${ag</w:t></w:r></w:p></w:tc>e}”),这就会导致FreeMarker
不能识别,那么就需要我们手动调整或者重复第二步重新生成,直到所有的占位符都正确。
第四步:创建一个项目,并引入FreeMarker的jar包。
<!--FreeMarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
注:本人创建的是maven项目(SpringBoot),直接在pom.xml中引入FreeMarker的依赖即可。
第五步:将xml文件,修改后缀为(freemarker模板的).ftl,并放入项目classpath下
的templates目录下。
第六步:编写导出word工具类
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import sun.misc.BASE64Encoder;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* freemarker + word模板 导出word
*
* @author JustryDeng
* @date 2018/11/14 17:41
*/
public class FreemarkeExportrWordUtil {
/** 默FreeMarker配置实例 */
private static final Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
/** 默认采用UTF-8编码 */
private static final String ENCODING = "UTF-8";
/** buffer */
private static final int BUFFER_SIZE = 1024;
/**
* 从指定freemarker模板所在文件夹
*
* “/” 对应 classpath
* “/templates/” 对应 classpath下的templates/
*/
private static final String DEFAULT_POSITION = "/";
static {
configuration.setDefaultEncoding(ENCODING);
}
/**
* 导出excel
*
* @param templateFileName
* 模板文件名(含后缀,如:abc.ftl)
* @param resultFileAllPathName
* 结果文件全路径文件名 (如: C:/Users/result.doc 再如: C:/Users/result.docx)
* @param dataObject
* 与模板中的占位符 对应的 数据信息(一般为:一个专门创建的对象, 或者是Map)
* @return 生成的word文件
* @throws IOException
* @throws TemplateException
* @date 2018/11/16 10:52
*/
public static File doExport(String templateFileName, String resultFileAllPathName, Object dataObject)
throws IOException, TemplateException {
return doExport(templateFileName, DEFAULT_POSITION, resultFileAllPathName, dataObject);
}
/**
* 导出excel
*
* @param templateFileName
* 模板文件名(含后缀,如:abc.ftl)
* @param templateFileDir
* 模板文件所在位置名(如: "/" 代表 classpath)
* @param resultFileAllPathName
* 结果文件全路径文件名 (如: C:/Users/result.doc 再如: C:/Users/result.docx)
* @param dataObject
* 与模板中的占位符 对应的 数据信息(一般为:一个专门创建的对象, 或者是Map)
* @return 生成的word文件
* @throws IOException
* @throws TemplateException
* @date 2018/11/16 10:52
*/
public static File doExport(String templateFileName, String templateFileDir,
String resultFileAllPathName, Object dataObject)
throws IOException, TemplateException {
// 指定模板文件所在 位置
configuration.setClassForTemplateLoading(FreemarkeExportrWordUtil.class, templateFileDir);
// 根据模板文件、编码;获取Template实例
Template template = configuration.getTemplate(templateFileName, ENCODING);
File resultFile = new File(resultFileAllPathName);
// 判断要生成的word文件所在目录是否存在,不存在则创建
if (!resultFile.getParentFile().exists()) {
boolean result = resultFile.getParentFile().mkdirs();
}
// 写出文件
try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(resultFile));
Writer writer = new BufferedWriter(osw, BUFFER_SIZE)) {
template.process(dataObject, writer);
}
return resultFile;
}
/**
* 获取图片对应的base64码
*
* @param imgFile
* 图片
* @return 图片对应的base64码
* @throws IOException
* @date 2018/11/16 17:05
*/
public static String getImageBase64String(File imgFile) throws IOException {
InputStream inputStream = new FileInputStream(imgFile);
byte[] data = new byte[inputStream.available()];
int totalNumberBytes = inputStream.read(data);
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
}
第七步:使用工具类导出word即可
使用测试:
测试示例一:基本测试
测试主函数为:
public static void main(String[] args) {
// 模板文件名
String templateFileName = "exportWordDemoTwo.ftl";
// 模板文件所在位置
String templateFileDir = "/templates/";
// 要生成的文件 全路径文件名
String fileName = "C:/Users/JustryDeng/Desktop/result.doc";
// 组装FreeMarker占位符对应的数据
Map<String, Object> dataMap = new HashMap<>(8);
dataMap.put("year", "2018");
dataMap.put("month", 11);
dataMap.put("day", 16);
dataMap.put("name", "JustryDeng");
dataMap.put("age", 24);
dataMap.put("handsomeValue", 100);
dataMap.put("isSingle", "是");
try {
FreemarkeExportrWordUtil.doExport(templateFileName, templateFileDir, fileName, dataMap);
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
执行主函数后,在指定位置就生成了对应的文件:
打开该文件:
测试示例二:集合数据测试
我们回到第二步、第三步;在xml中添加对应的freemarker标签:
其他的不变。这次将该xml后缀名改为ftl,这次放入classpath下(这样做纯粹是为了使用一下工具类中的另一个方法):
测试主函数为:
public static void main(String[] args) {
// 模板文件名
String templateFileName = "exportWordDemoTwo.ftl";
// 要生成的文件 全路径文件名
String fileName = "C:/Users/JustryDeng/Desktop/result123.doc";
// 组装FreeMarker占位符对应的数据
// 组装FreeMarker占位符对应的数据
Map<String, Object> dataMap = new HashMap<>(8);
dataMap.put("year", "2018");
dataMap.put("month", 11);
dataMap.put("day", 16);
List<Map<String, Object>> list = new ArrayList<>(8);
dataMap.put("personList", list);
for (int i = 0; i < 10; i++) {
Map<String, Object> tempMap = new HashMap<>(8);
tempMap.put("name", "JustryDeng");
tempMap.put("age", 24);
tempMap.put("handsomeValue", 90 + i);
tempMap.put("isSingle", "是");
list.add(tempMap);
}
try {
FreemarkeExportrWordUtil.doExport(templateFileName, fileName, dataMap);
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
执行主函数后,在指定位置就生成了对应的文件:
打开该文件:
测试示例三:导出图片测试
同样的,先在word中放一张图片:
然后在第三步时,将xml中该图片对应的base64码替换为freemarker对应的占位符:
替换结果如图所示:
其他的不变。将该xml后缀名改为ftl,并将模板文件放入classpath下:
测试主函数为:
public static void main(String[] args) {
// 模板文件名
String templateFileName = "exportWordDemoThree.ftl";
// 要生成的文件 全路径文件名
String fileName = "C:/Users/JustryDeng/Desktop/resultImage.doc";
// 组装FreeMarker占位符对应的数据
Map<String, Object> dataMap = new HashMap<>(8);
dataMap.put("year", "2018");
dataMap.put("month", 11);
dataMap.put("day", 16);
dataMap.put("name", "JustryDeng");
dataMap.put("age", 24);
dataMap.put("handsomeValue", 100);
dataMap.put("isSingle", "是");
try {
File imageFile = new File("C:/Users/JustryDeng/Desktop/66.png");
dataMap.put("myImage", FreemarkeExportrWordUtil.getImageBase64String(imageFile));
} catch (IOException e) {
e.printStackTrace();
}
try {
FreemarkeExportrWordUtil.doExport(templateFileName, fileName, dataMap);
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
执行主函数后,在指定位置就生成了对应的文件:
打开该文件:
注:实际上,导出的word里面的图片,宽高还是原来模板图片的大小;也就是说:这可能导致导出的word里面的图片出
现变形。
解决图片变形:
指定图片的宽高:
在xml中对应设置宽高的位置,使用freemarker占位符:
在程序中指定图片的宽高:
public static void main(String[] args) {
// 模板文件名
String templateFileName = "exportWordDemoThree.ftl";
// 要生成的文件 全路径文件名
String fileName = "C:/Users/JustryDeng/Desktop/resultImage.doc";
// 组装FreeMarker占位符对应的数据
Map<String, Object> dataMap = new HashMap<>(8);
dataMap.put("year", "2018");
dataMap.put("month", 11);
dataMap.put("day", 16);
dataMap.put("name", "JustryDeng");
dataMap.put("age", 24);
dataMap.put("handsomeValue", 100);
dataMap.put("isSingle", "是");
try {
File imageFile = new File("C:/Users/JustryDeng/Desktop/66.png");
BufferedImage bufferedImage = ImageIO.read(imageFile);
// 单位为 px
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
// 设置图片的大小
dataMap.put("width", width + "px");
dataMap.put("height", height + "px");
dataMap.put("myImage", FreemarkeExportrWordUtil.getImageBase64String(imageFile));
} catch (IOException e) {
e.printStackTrace();
}
try {
FreemarkeExportrWordUtil.doExport(templateFileName, fileName, dataMap);
} catch (IOException | TemplateException e) {
e.printStackTrace();
}
}
提示:由于freemarker模板走的其实是XML,所以如果想输出类似于“>“、“<”这样的XML敏感的字符的话,
需要将“<”写为“<”、“>”写为“>”,如:
^_^ 如有不当之处,欢迎指正
^_^ 本文已经被收录进《程序员成长笔记(三)》,笔者JustryDeng
来源:CSDN
作者:justry_deng
链接:https://blog.csdn.net/justry_deng/article/details/84144023