FreeMaker 导出自定义样式word (SpringBoot)图片、文本块、集合循环(详细教程)

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();
        }
        //设置异常处理器 这样的话 即使没有属性也不会出错 如:${list.name}...不会报错
        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')}
等等。。。。。。具体的可以百度一下
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringBoot_Freemarker生成Word_多个表格+两层嵌套循环; 步骤说明: 1.用Microsoft Office Word打开word原件;将文档中需要动态生成的内容,替换为属性名 ${name} 2.另存为,选择保存类型Word 2003 XML 文档(*.xml) 3.用Firstobject free XML editor打开文件,选择Tools下的Indent【或者按快捷键F8】格式化文件内容。左边是文档结构,右边是文档内容; 4. 文档生成后有时需要手动修改,查找第一步中设置的属性名,可能会产生类似${n.....ame}类似的样子,我们将将名字中间的标签删掉,恢复为${name} 5. word模板中有表格,需要循环的位置, 用 标签将第二对 标签(即除表头的w:tr标签后的一对)包围起来 同时表格内的属性例如${name},在这里需要修改为${user.name} (userList是集合在dataMap中的key, user是集合中的每个元素, 类似), 如图: PLUS:若表格之外还有嵌套的循环,也需要用,注意这里的标签不要和某对其他标签交叉,不可以出现这种 6. 标识替换完之后,另存为.ftl后缀文件即可。 代码里是相对有一丢丢复杂的,两层嵌套循环; 总(dataMap) deptName 部门名 list(Table)表的集合 table1(map) table-名字 ${map.table} tableName-中文名 ${map.tableName} columnCount-字段数 ${map.columnCount} recordCount-记录数 ${map.recordCount} listA-List--表格1 map.listA column Model属性——字段名 ${model.column} columnName Model属性——字段中文名 ${model.column} rate Model属性——字段占比 ${model.rate} nullValueCount Model属性——字段空值数 ${model.nullValueCount} listB-List--表格2 map.listB …… listC-List--表格3 map.listC …… table2 table-名字 ${map.table} tableName-中文名 ${map.tableName} columnCount-字段数 ${map.columnCount} recordCount-记录数 ${map.recordCount} listA-List--表格1 map.listA column Model属性——字段名 ${model.column} columnName Model属性——字段中文名 ${model.column} rate Model属性——字段占比 ${model.rate} nullValueCount Model属性——字段空值数 ${model.nullValueCount} listB-List--表格2 map.listB …… listC-List--表格3 map.listC …… table3 ……
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值