FreeMarker:使用word模版文件批量替换动态数据

1、导入依赖

        <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.29</version>
        </dependency>

2、准备word模版文件

2.1、新建word,并且编辑占位符,使用 “${ }”包裹占位符,如: ${name}、${age}

2.2、另存为 Word XML 格式,该格式可以与 word文件格式、xml格式无损转换,文件不会变形

2.3、打开XML文件,把图片编码替换为图片占位符

2.3.1        图片编码:非常长的这一串就是图片编码,搜索 binaryData就可以找到,找到后删除

 2.3.2、删除图片编码后,在原位置添加占位符,如:${icon}

2.4、把修改好的模版复制到/resrouce目录下,并且修改后缀为 .ftl

ftl格式为FreeMarker可编辑的文本类型,并且与xml格式文件可以无损转换

 

3、批量替换动态数据

package shzu.wangbao.autoGenerateWordFile.util;


import freemarker.core.ParseException;
import freemarker.log.Logger;
import freemarker.template.*;
import lombok.extern.slf4j.Slf4j;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName: FreeMarkerUtil
 * @Description: 使用 FreeMarker 批量修改替换动态文本和图片
 * @Date: 2023/3/20  6:35 PM
 * @Author: wang-bao / uuid-07c6
 **/

public class FreeMarkerUtil {

    /**
     * 1、新建配置类对象 config , 用来管理 FreeMarker 的配置信息,非常重要!!!
     */
    private Configuration config = null;
    /**
     * 2、新建无参构造器,用于初始化 FreeMarker 的配置信息
     */
    public FreeMarkerUtil() {
        //  设置 FreeMarker 的版本 ==> 2.3.29  ==> 这个要根据你引入 FreeMarker 的依赖版本来切换!!!
        config = new Configuration(Configuration.VERSION_2_3_29);
        //  设置 FreeMarker 的默认编码格式 ==> utf-8
        config.setDefaultEncoding("utf-8");
    }
    /**
     * 3、新建日志 log ,用来打印各种日志信息,比如错误信息和异常信息,便于查找问题和修正代码
     */
    private Logger log = Logger.getLogger(FreeMarkerUtil.class.toString());


    /**
     * 4、使用 FreeMarker 生成 Word 文件
     * @param dataMap      数据
     * @param templateName 目标名(模板名称)
     * @param saveFilePath 要保存的文件存放地址:全路径+文件名
     */
    public File createWord(Map<String, Object> dataMap, String templateName, String saveFilePath) {

        //  一、设置配置信息
        //  4-1、设置 word 模板文件存放的位置,默认寻找 /resources 目录下的位置
        //  此处我选择的是 "/resources/templates/" ,因为默认省去 "/resources" 的前缀,直接填写参数 "/templates/"
        config.setClassForTemplateLoading(this.getClass(), "/templates/");
        //  4-2、设置异常处理器 这样的话 即使没有属性也不会出错 如:${list.name}...不会报错
        config.setTemplateExceptionHandler(TemplateExceptionHandler.IGNORE_HANDLER);

        //  二、设置提供原始数据的源文件
        //  4-3、新建模版类对象 template,用来管理模版文件,完成动态数据的替换
        Template template = null;
        //  4-4、查找后缀为 ".ftl" 的文件,截取其文件名
        if (templateName.endsWith(".ftl")) {
            templateName = templateName.substring(0, templateName.indexOf(".ftl"));
        }
        //  4-5、通过文件名 templateName ,获取模版文件的数据流 template,此后调用输入流的时候会直接从 template 中获取并填充
        try {
            template = config.getTemplate(templateName + ".ftl");
        } 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();
        }

        //  三、设置被写入数据的目标文件的写入流 BufferedWriter
        //  4-6、新建我们需要生成的文件 outFile,指定其存储位置 saveFilePath
        File outFile = new File(saveFilePath);
        //  4-7、如果父节点的文件夹不存在,则新建该文件夹
        if (!outFile.getParentFile().exists()) {
            outFile.getParentFile().mkdirs();
        }
        //  4-8、新建文件输出流对象 fos,用于对 word 文件写入数据
        //  Stream 是按照字节逐个读取,读取汉字时会出现乱码( 1个汉字 = 2个字节 )
        FileOutputStream fos = null;
        //  4-9、指定被写入数据流的文件对象为 outFile
        try {
            fos = new FileOutputStream(outFile);
        } catch (FileNotFoundException e) {
            log.error("输出文件时未找到文件", e);
            e.printStackTrace();
        }
        //  4-10、新建写入流对象 out,用于对 word 文件写入数据流
        //  Writer 是按照字符逐个读取,可以保证汉字不会乱码( 1个汉字 = 2个字节 )
        Writer out = null;
        //  4-11、使用 BufferedWriter 包装文件流输出对象
        //  BufferedWriter 是 Writer 的子类,以缓冲区为单位逐次写入数据,可以提高写入数据的效率
        out = new BufferedWriter(new OutputStreamWriter(fos));

        //  四、生成目标文件
        //  4-12、读取源文件数据,执行目标文件的写入流,完成动态数据的批量替换
        try {
            template.process(dataMap, out);
        } catch (TemplateException e) {
            log.error("填充模板时异常", e);
            e.printStackTrace();
        } catch (IOException e) {
            log.error("IO读取时异常", e);
            e.printStackTrace();
        }
        log.info("由模板文件:" + templateName + ".ftl" + " 生成文件 :" + saveFilePath + " 成功!!");
        //  4-13、关闭写入流
        try {
            out.close();//web项目不可关闭
        } catch (IOException e) {
            log.error("关闭Write对象出错", e);
            e.printStackTrace();
        }
        //  4-14、返回目标文件
        return outFile;
    }

    /**
     * 5、获取图片对应的 base64 编码(图片在xml文件中存储为 base64 格式的图片编码)
     * @param imgFile 图片
     * @return 图片对应的base64码
     * @throws IOException
     * @date 2019/4/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);
    }

    /**
     * 6、主方法,测试工具类是否生效
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        //  1、设置常用的地址前缀
        String imagePath = "/Users/never/Desktop/gitbase/autoGenerateWordFile/src/main/resources/static/images";
        String docPath = "/Users/never/Desktop/gitbase/autoGenerateWordFile/src/main/resources/static/docs";
        //  2、新建工具类
        FreeMarkerUtil emw = new FreeMarkerUtil();
        //  3、使用 map 存储动态数据与占位符的映射关系
        Map<String, Object> dataMap = new HashMap<String, Object>();
        //  4、设置字符串占位符与字符串数据的映射关系
        dataMap.put("name", "王某某");
        dataMap.put("sex", "男");
        dataMap.put("age","23");
        //  5、获取 Base64 格式的图片编码
        String imageStr = getImageBase64String(new File(imagePath+"/322.jpeg"));
        //  6、设置图片占位符与图片编码的映射关系
        dataMap.put("icon", imageStr);
        //  7、使用createWord方法,生成 (.doc 格式) word 文件
        //  第一个参数是map,第二个参数是源文件(ftl文件),第三个参数是新生文件(产生的目标文件的存储路径)
        emw.createWord(dataMap, "demo666.ftl", docPath+"/demo666.doc");
    }
}


4、运行效果

 5、可能出现的问题

如:格式解析失败

解决方法:打开xml文件,查看${}占位符是否与动态数据(如:name) 分隔开

问题原因:复制粘贴导致的文本变形,${name}被分割成${}和name

解决方案:删除占位符,重新手打一遍

经验小结:不要复制粘贴!不要复制粘贴!不要复制粘贴!动态数据一定要手打,防止格式变形。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值