• 根据模板动态生成word(一)使用freemarker生成word


    @

    一、准备模板

    1、创建模板文件

    首先先建立一个word文件,输入模板内容freemaker的内容,下面是本次演示的word文件。
    docx格式演示word模板

    然后将word文件另存为 .xml 文件,然后再把文件后缀改成.ftl 。将项目的resource目录下建立一个templates目录(非必须步骤)将 模板文件放到templates目录下
    请添加图片描述

    请添加图片描述
    打开模板文件按 Ctrl + Shift + L 将模板内容格式化。

    2、处理模板

    2.1 处理普通文本

    处理文本比较简单,将需要替换文本中直接用占位符 ${} 替换即可。

    这里发现一个问题因为之前在word格式时我就已经替换了变量,但是在ftl变量却被 拆分成多段了(见图1)。但是这样是freemaker是无法成功替换变量的,所以需要手动处理成到一个段里(如图2),关于这点实在太无语了,因为没有找到比较好的处理办法,只能手工处理,在实际的开发工作中曾经花了几个小时来做这件事情。
    图1:
    请添加图片描述

    图2
    请添加图片描述

    2.2 处理表格

    如果模板里需要用变量填充表格,建议模板里的表格像word文件一样建一个两行的表格。在模板中<> 表示一个表格 、 表示一行、 表示一列。因为FreeMarker 是利用列表一行一行循环填充的,所以我们可以根据关键字找到<>标签,因为第一个 是表头注意不要改到了,找到第二个在前后分别加上如下语句即可,后面的表格里后面的行需要删掉,建议模板里的表格像word文件一样建一个两行的表格即可这样就不用删了:

    <#list itemList as item>
    
    

    替换后的模板如下:
    请添加图片描述

    2.3 处理图片

    如果模板里需要用变量填充图片,建议先在word文件里插入一张图片,这样在模板文件里找到<pkg:binaryData>标签直接里面把里面的图片base64字符替换成变量即可,word里可以通过植入base64字符来展示图片:

    替换前:
    请添加图片描述

    替换后:

    <pkg:binaryData>${image1}pkg:binaryData>
    

    到此模板已经调整完成,接下来就可以开始写代码了。

    二、项目代码

    1、引入依赖

    在项目的pom文件里引入如下依赖

    	<dependency>
              <groupId>org.freemarkergroupId>
              <artifactId>freemarkerartifactId>
              <version>2.3.31version>
          dependency>
    

    2、生成代码

    将图片转成Base64字符串的公共方法:

    public static String getImageBase64Str(String imgFile) {
            try( InputStream in = new FileInputStream(imgFile)) {
                byte[] data = new byte[in.available()];
                in.read(data);
                BASE64Encoder encoder = new BASE64Encoder();
                return encoder.encode(data);
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    

    根据模板文件生成word,主要生成的word的文件后缀必须是doc不能是docx,不然生成的文件无法打开。

    public static void crateWord(Map dataMap, String templatePath, String targetFile){
            String path = templatePath.substring(0,templatePath.lastIndexOf("/"));
            String templateName = templatePath.substring(templatePath.lastIndexOf("/") + 1);
            try (FileOutputStream out = new FileOutputStream(targetFile);
                 Writer writer = new BufferedWriter(new OutputStreamWriter(out, "utf-8"))){
                Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
                configuration.setDefaultEncoding("utf-8");
                configuration.setClassForTemplateLoading(FreeMakerTest.class, path);
                //除了ClassForTemplateLoading外,另一种模板加载方式DirectoryForTemplateLoading,也可用
                //ClassPathResource resource = new ClassPathResource(path);
                //configuration.setDirectoryForTemplateLoading(resource.getFile());
                //加载模板
                Template template = configuration.getTemplate(templateName);
                //渲染模板
                template.process(dataMap, writer);
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (TemplateException e) {
                throw new RuntimeException(e);
            }
        }
    

    三、验证生成word

    新建的列表数据实体类:

    public class Arrears{
        private String name;
        private Integer num;
    
        private String endDay;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getNum() {
            return num;
        }
    
        public void setNum(Integer num) {
            this.num = num;
        }
    
        public String getEndDay() {
            return endDay;
        }
    
        public void setEndDay(String endDay) {
            this.endDay = endDay;
        }
    }
    

    准备模板填充数据

    private static Map prepareParam(){
            LocalDate currentDate = LocalDate.now();
            LocalDate endDate = currentDate.plusYears(1L);
            List arrearsList = new ArrayList<>();
            arrearsList.add(new Arrears(){{setName("一顿老魏");setNum(1);setEndDay("三月内");}});
            arrearsList.add(new Arrears(){{setName("贵州大黄牛");setNum(1);setEndDay("一年内");}});
            arrearsList.add(new Arrears(){{setName("v我50");setNum(1);setEndDay("一月内");}});
    
            //填充所需要的数据
            Map dataMap = new HashMap<>();
            dataMap.put("debtor", "陈有楚");
            dataMap.put("nowYear", String.valueOf(currentDate.getYear()));
            dataMap.put("nowMonth", String.valueOf(currentDate.getMonthValue()));
            dataMap.put("nowDay", String.valueOf(currentDate.getDayOfMonth()));
            dataMap.put("arrears", "一顿老魏、贵州大黄牛、v我50");
            dataMap.put("endYear", String.valueOf(endDate.getYear()));
            dataMap.put("endMonth", String.valueOf(endDate.getMonthValue()));
            dataMap.put("endDay", String.valueOf(endDate.getDayOfMonth()));
            dataMap.put("arrearsList", arrearsList);
            dataMap.put("image1", getImageBase64Str("D:\\picture\\其他\\24-05-23-142418.png"));
            dataMap.put("creditor", "知北游");
            return dataMap;
        }
    

    测试代码:

    public static void main(String[] args) throws IOException {
            //准备参数
            Map dataMap = prepareParam();
            crateWord(dataMap,"/templates/qiantiao.ftl","D:\\test\\qiantiao.doc");
        }
    

    测试结果:
    请添加图片描述

  • 相关阅读:
    【如何学习CAN总线测试】——CAN物理层测试
    Linux拔网线后网卡仍然处于激活状态
    昱琛航空IPO被终止:曾拟募资5亿 郭峥为大股东
    第二章 数据库设计
    Crystal Ball—甲骨文水晶球风险管理软件(概念以及实战——中级案例篇)
    ESP8266,手机与电脑之间的TCP通讯
    pdf.js不分页渲染(渲染完整内容)
    如何实现暗示文本顺序
    react 手写树形渲染组件
    深度学习九 —— 手撕 一维离散序列 的线性卷积 和 互相关
  • 原文地址:https://www.cnblogs.com/fhey/p/17539412.html