转载:https://blog.youkuaiyun.com/Alexshi5/article/details/84437300
一、制作.ftl后缀的word模板文件
1、新建一个word文档模板
使用其他文本编辑器编写表达式,如:Editplus
2、将word文档另存为xml并改名为.ftl后缀的文件
另存完之后关闭word文档,将demo.xml的后缀修改为.ftl,然后使用文本编辑器打开demo.ftl文件
3、修改.ftl文件并生成最终的模板文件
① 修改图片的数据内容使用表达式代替
替换之后如下:
② 在数据表格中添加循环标签
二、通过模板文件生成word文档
1、添加pom.xml的依赖
-
<dependency>
-
<groupId>org.freemarker
</groupId>
-
<artifactId>freemarker
</artifactId>
-
<version>2.3.23
</version>
-
</dependency>
2、index.jsp中添加一个Echarts图形报表
-
var option = {
-
angleAxis: {
-
type:
'category',
-
data: [
'周一',
'周二',
'周三',
'周四',
'周五',
'周六',
'周日'],
-
z:
10
-
},
-
radiusAxis: {
-
},
-
polar: {
-
},
-
series: [{
-
type:
'bar',
-
data: [
1,
2,
3,
4,
3,
5,
1],
-
coordinateSystem:
'polar',
-
name:
'A',
-
stack:
'a'
-
}, {
-
type:
'bar',
-
data: [
2,
4,
6,
1,
3,
2,
1],
-
coordinateSystem:
'polar',
-
name:
'B',
-
stack:
'a'
-
}, {
-
type:
'bar',
-
data: [
1,
2,
3,
4,
1,
2,
5],
-
coordinateSystem:
'polar',
-
name:
'C',
-
stack:
'a'
-
}],
-
legend: {
-
show:
true,
-
data: [
'A',
'B',
'C']
-
}
-
};
-
-
var myChart = echarts.init(
document.getElementById(
"content"));
-
myChart.setOption(option);
-
//获取Echart图形报表生成的Base64编码格式的数据
-
var imgData = myChart.getConnectedDataURL();
-
$.post(
'/demo/word',{
'imgData':imgData},
function (data) {
-
alert(data);
-
},
'json');
3、后台处理请求并设置模板数据
-
@Controller
-
@RequestMapping("/demo")
-
public
class DemoController {
-
-
@RequestMapping("/word")
-
@ResponseBody
-
public String generateWord(String imgData){
-
// 传递过程中 "+" 变为了 " " ,所以需要替换
-
String newImageInfo = imgData.replaceAll(
" ",
"+");
-
// 数据中:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABI4AAAEsCAYAAAClh/jbAAA ...
-
// 在"base64,"之后的才是图片信息
-
String[] arr = newImageInfo.split(
"base64,");
-
-
//添加模板数据
-
Map<String,Object> dataMap =
new HashMap<>();
-
dataMap.put(
"userName",
"张三");
-
dataMap.put(
"imgData",arr[
1]);
-
-
Person person1 =
new Person(
"李四",
"男",
36,
"18811240001");
-
Person person2 =
new Person(
"王五",
"女",
22,
"18811240002");
-
Person person3 =
new Person(
"赵六",
"男",
46,
"18811240003");
-
List<Person> personList =
new ArrayList<>();
-
personList.add(person1);
-
personList.add(person2);
-
personList.add(person3);
-
dataMap.put(
"personList",personList);
-
-
//文件生成路径
-
String wordFilePath =
"E:\\ftl";
-
//文件生成名称(因为是2003版本的xml模板,这里使用.doc后缀,如果使用.docx后缀生成的文件有问题)
-
String wordFileName =
"演示文档.doc";
-
//模板路径
-
String templatePath =
"E:\\ftl";
-
//模板文件名称
-
String templateFileName =
"demo.ftl";
-
-
//生成word文档
-
Boolean result = WordUtil.writeWordReport(wordFilePath, wordFileName, templatePath, templateFileName, dataMap);
-
if(result){
-
return
"success";
-
}
else {
-
return
"error";
-
}
-
}
-
}
4、生成word文档的工具类方法
-
/**
-
* 根据freemarker生成word文档并存到指定目录
-
* @param wordFilePath word文件生成的目录
-
* @param wordFileName word文件名
-
* @param templatePath 模板文件所在的目录
-
* @param templateFileName 模板文件名
-
* @param beanParams 生成word文件所需要的模板数据
-
* @return
-
*/
-
public static Boolean writeWordReport(String wordFilePath,String wordFileName,
-
String templatePath,String templateFileName, Map<String, Object> beanParams) {
-
Configuration config =
new Configuration(Configuration.getVersion());
-
Writer out =
null;
-
try {
-
config.setDirectoryForTemplateLoading(
new File(templatePath));
-
Template template = config.getTemplate(templateFileName,
"UTF-8");
-
-
//获取文件目录,如果不存在则创建
-
String filePath =
"";
-
int index = wordFilePath.lastIndexOf(File.separator);
-
if(index != wordFilePath.length()-
1){
-
filePath = wordFilePath+ File.separator;
-
}
else {
-
filePath = wordFilePath;
-
}
-
File file1 =
new File(filePath);
-
if(!file1.exists()){
-
file1.mkdirs();
-
}
-
-
//输出文件
-
File file =
new File(filePath+wordFileName);
-
FileOutputStream fos =
new FileOutputStream(file);
-
out =
new OutputStreamWriter(fos,
"UTF-8");
-
template.process(beanParams, out);
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
finally{
-
try {
-
if(out !=
null) {
-
out.close();
-
}
-
}
catch (IOException e) {
-
e.printStackTrace();
-
}
-
}
-
}
5、最终生成的word文档如下:
6、补充一:解决传空值导致程序报空指针异常的问题
如果生成文档时报如下错误:
Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
这有可能是你在传值过程中传了空值,导致模板引擎无法获取参数值,这时可以将表达式更改为${person.name?default('')}这种形式,这个表达式的意思是如果为空值就替换成'', 这样即便不小心传了空值也不会导致程序出错。
7、补充二:解决导出图片缺失或图片不完整问题
实际开发中会遇到有一些图形报表导出图片时不完整,比如:折线图导出时只有点却没有线条,这是由于折线图有动画,而生成的图片是在动画还未结束时就生成的。解决方法就是在配置项中关闭动画效果。
8、补充三 :解决循环添加多张图片重复问题
按照上面的思路,既然可以循环添加数据,那肯定也可以循环添加图片,但是当我们使用循环标签添加图片时,却发现生成的多张图片都是一样的,这是由于我们没有修改模板文件中的图片标签的属性导致的。需要修改的地方有两个一个是:<w:binData></w:binData>标签的w:name属性,一个是:<v:imagedata></v:imagedata>标签的src属性。示例如下:
上面这个属性的含义指的是当前文档的第几张图片,如果我们的图片集合数据从下标0开始则这里的后缀要修改成"下标+1"的形式。伪代码示例如下:
-
<#list dataList as obj>
<!--循环开始,obj_index代表当前循环对象的索引,从下标0开始-->
-
<!--伪代码-->
-
<w:binData w:name="${"wordml://0300000"+ obj_index+1 +".png"}" xml:space="preserve">${obj.imgUrl?default('')}
</w:binData>
-
<v:imagedata src="${"wordml://0300000"+ obj_index+1 +".png"}" o:title=""/>
-
<#/list>
<!--循环结束-->
如果文档前面已经有一张图片了,然后才开始循环图片呢?那循环开始的图片就是当前文档的第二张图片,那后缀就要改成“下标+2”的形式,以此类推。示例如下:
参考: