FreeMaker 导出自定义样式word (SpringBoot)
第一步、准备模板文件创建doc文档
这是自己准备的doc文档每一块要填的内容都用${ }来代替,因为后面填充数据的时候遇到这个符号都会去填充对应的数据。我这个模板里用的都是表格因为表格设计方便,只要把表格的边框设置不显示就行了。下面教育经历跟工作经历都是可以循环显示的。用这种方法可以解决很多模板需求。里面包含了图片、文本块、循环显示内容 。
第二步、将模板文件doc另存为xml格式的,如下图所示
第三步、将上面另存的xml文件后缀改成ftl的,如下图所示
第四步、打开ftl文件修改上面doc文档中的一些内容
首先要修改doc中的图片的东西,修改的过程比较麻烦 ,容易看花眼。但是不难
第一修改图片 找到你doc中所插入图片的那个位置做如下修改,图片一般在binaryData标签中,这个标签里面会有一大串的base64的编码找到将其删除然后替换成这里的${img}标签 ,名字可以换成其他的。
第二、修改表格中的不用循环的部分
例如下面的现居住地,找到这个标签后最好加上这个?if_exists
说明一下:这个?if_exists是用来判断是否为空的 是freemaker里的语法,最好加上,因为如果后端那边传的这个areaName是空的话 ,xml里没有判空会出错。
第三、修改表格中循环的部分,如图所示
找到需要循环表格的部分通过标签<w:tbl></w:tbl>来找,创建doc设计的时候尽量不要嵌套太多的表格不然找出需要循环的那一部分的表格很麻烦,因为word保存的xml格式的东西打开看很乱。
第五步、将改完的ftl文件保存到项目中的tempaltes下
ftl文件也可以放在服务器的某个文件夹下 ,方便需求变更替换。我这里是放在项目的templates文件夹下的。
第六步、创建一个Springboot的项目
第七步、导入freemaker的依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
第八步、编写工具类
/**
* @Description 利用FreeMarker导出Word
* 2021年1月7日
* @Author shandl
*/
public class ExportMyWord {
private Logger log = Logger.getLogger(ExportMyWord.class.toString());
private Configuration config = null;
public ExportMyWord() {
//版本选择,不要低于2.3的2.3以下的可能会有问题
config = new Configuration(Configuration.VERSION_2_3_30);
config.setDefaultEncoding("utf-8");
}
/**
* FreeMarker生成Word
* @param dataMap 数据
* @param templateName 目标名
* @param saveFilePath 保存文件路径的全路径名(路径+文件名)
* @Author shandl 2021年1月7日
*/
public void createWord(Map<String, Object> dataMap, String templateName, String saveFilePath) {
//加载模板(路径)数据
// config.setClassForTemplateLoading(this.getClass(), "");
//config加载模板数据的方法有三种,这边用的是通过文件路劲的方式 也是比较好的,因为模板文件你可以指定的放在服务器的某个文件夹下 不会乱。这里是放在项目的templates下的为了写demo方便的。
String path = Thread.currentThread().getContextClassLoader().getResource("").getPath()+"templates";
try {
config.setDirectoryForTemplateLoading(new File(path));
} catch (IOException e) {
e.printStackTrace();
}
//设置异常处理器 这样的话 即使没有属性也不会出错 如:${}...不会报错
config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);
Template template = null;
Writer out = null;
FileOutputStream fos = null;
try {
template = config.getTemplate(templateName);
File outFile = new File(saveFilePath);
if(!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
}
fos = new FileOutputStream(outFile);
out = new BufferedWriter(new OutputStreamWriter(fos));
//将模板中的预先的代码替换为数据
template.process(dataMap, out);
} catch (TemplateNotFoundException e) {
log.error("模板文件未找到", e);
e.printStackTrace();
} catch (MalformedTemplateNameException e) {
log.error("模板类型不正确", e);
e.printStackTrace();
} catch (ParseException e) {
log.error("解析模板出错,请检查模板格式", e);
e.printStackTrace();
} catch (IOException e) {
log.error("IO读取失败", e);
e.printStackTrace();
} catch (TemplateException e) {
log.error("填充模板时异常", e);
e.printStackTrace();
} catch (Exception e) {
log.error("下载错误", e);
e.printStackTrace();
} finally {
try {
out.close();
}catch (Exception e) {
log.error("关闭流错误", e);
e.printStackTrace();
}
}
log.info("由模板文件:" + templateName + ".ftl" + " 生成文件 :" + saveFilePath + " 成功!!");
}
/**
* 获得图片的Base64编码
* @param imgFile
* @return
* @Author shandl 2021年1月8日
*/
public String getImageStr(String imgFile) {
InputStream in = null;
byte[] data = null;
try {
// 构造URL
URL url = new URL(imgFile);
// 打开连接
URLConnection con = url.openConnection();
// 输入流
in = con.getInputStream();
// in = new FileInputStream(imgFile);
} catch (FileNotFoundException e) {
log.error("加载图片未找到", e);
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
try {
data = new byte[in.available()];
//注:FileInputStream.available()方法可以从输入流中阻断由下一个方法调用这个输入流中读取的剩余字节数
in.read(data);
in.close();
} catch (IOException e) {
log.error("IO操作图片错误", e);
e.printStackTrace();
}
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(data);
}
}
第九步、编写对应的Controller
这里是前端后端分离的,测试可以用postman测试
/**
* @Description 在线简历操作
* @Author shandl
* @Version V2.0.0
* @Since 2.0
* @Date 2021/1/7
*/
@RequestMapping("/Re")
@RestController
public class RecruitOnlineResumeController {
@Autowired
RecruitResumeService resumeService;
@ApiOperation("下载在线简历")
@PostMapping("/downOnlieResume")
public ReturnBody downOnlieResume(@RequestParam("resumeId") int resumeId) {
if (StringUtils.isNotBlank(resumeId + "")) {
RecruitResume recruitResume = resumeService.getResumeInfoById(resumeId);
if (recruitResume != null) {
List<HbRecruitResumeEdu> edus = recruitResume.getEdus();
List<HbRecruitResumeWork> works = recruitResume.getWorks();
ExportMyWord emw = new ExportMyWord();
Map<String, Object> dataMap = new HashMap<String, Object>();
//个人头部信息
dataMap.put("userName", recruitResume.getUserName());
dataMap.put("email", recruitResume.getEmail());
dataMap.put("userGender", recruitResume.getUserGender());
dataMap.put("img", emw.getImageStr("D:/test/pic/1.jpg"));
dataMap.put("phoneNumber", recruitResume.getPhoneNumber());
dataMap.put("areaName", recruitResume.getProvinceName() + "-" + recruitResume.getAreaName());
dataMap.put("userAge", DateUtil.computeAge(recruitResume.getBirthday(), new Date()));
dataMap.put("birth", recruitResume.getBirthday());
dataMap.put("workTime", recruitResume.getWorkExperience());
//最近工作
dataMap.put("latestPosition", works.get(0).getJobName());
dataMap.put("latestCompany", works.get(0).getCompany());
//最高学历
dataMap.put("highestMajor", edus.get(0).getMajor());
dataMap.put("highestSchool", edus.get(0).getSchool());
dataMap.put("highestDegree", edus.get(0).getTypeDesc());
//教育经历
dataMap.put("edus", edus);
//工作经历
dataMap.put("works", works);
String reName = "D:/" + recruitResume.getAreaName() + "-" + recruitResume.getUserName() + "-" + recruitResume.getWorkExperience() + UUIDUtils.getUUID(2) + ".doc";
emw.createWord(dataMap, "resumev1.ftl", reName);
return ReturnBody.ok().setData(reName);
}
}
return ReturnBody.error();
}
}
第十步、查看结果
补充知识:freemarker基础语法
(1)对List集合进行取值
<#list list集合 as item>
${item} --取值
</#list>
(2)对Map集合进行取值
<#list map?keys as key>
${key}:${map[key]}
</#list>
(3) if - else
格式:
<#if 条件>
输出
<#else>
输出
</#if>
(5)${封装对象.属性}
(6)${date?String('yyyy-MM-dd')}
等等。。。。。。具体的可以百度一下