@

一、准备模板

1、创建模板文件

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

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



打开模板文件按 Ctrl + Shift + L 将模板内容格式化。

2、处理模板

2.1 处理普通文本

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

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

图1:

图2

2.2 处理表格

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

<#list itemList as item>
</#list>

替换后的模板如下:

2.3 处理图片

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

替换前:

替换后:

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

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

二、项目代码

1、引入依赖

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

	<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</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<String, Object> 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<String, Object> prepareParam(){
LocalDate currentDate = LocalDate.now();
LocalDate endDate = currentDate.plusYears(1L);
List<Arrears> 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<String, Object> 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<String, Object> dataMap = prepareParam();
crateWord(dataMap,"/templates/qiantiao.ftl","D:\\test\\qiantiao.doc");
}

测试结果:

根据模板动态生成word(一)使用freemarker生成word的更多相关文章

  1. freemarker动态生成word并将生成的word转为PDF,openoffice转换word乱码

    之前项目有个需求,需要先动态生成word内容,然后再预览生成word的内容(不能修改).整理一下,方便以后使用. 网上参考了好多大神的博客.具体也忘了参考谁的了,如有侵权,请告知修改. 思路一: 将目 ...

  2. JSP利用freemarker生成基于word模板的word文档

    利用freemarker生成基于word模板的word文档 freemarker简介 FreeMarker是一个用Java语言编写的模板引擎,它基于模板来生成文本输出.FreeMarker与Web容器 ...

  3. 通过freemarker生成一个word,解决生成的word用wps打开有问题的问题,解决出word时中文文件名乱码问题,解决打开出word时打开的word出现问题的问题,出图片,解决动态列表

     通过freemarker制作word比较简单 步骤:制作word模板.制作方式是:将模板word保存成为xml----在xml的word模板中添加相应的标记----将xml的word文件的后缀名 ...

  4. 使用freemarker生成word,步骤详解并奉上源代码

    1.   步骤 1.    用word编辑好模板 1. 普通字符串替换为 ${string} 2. 表格循环用标签 <#list userList as user> 姓名:${user.u ...

  5. Freemaker基于word模板动态导出压缩文件汇总整理

    Freemaker基于word模板动态导出压缩文件汇总整理 Freemaker基于word模板动态导出单个文件思路和代码详情见连接: https://www.cnblogs.com/lsy-blogs ...

  6. Freemaker基于word模板动态导出汇总整理

    Freemaker基于word模板动态导出汇总整理 一.使用的jar包: 二.Word模板动态导出的基本思路: 1.首先通过自己在word中创建好需要导出的word文本+表格的模板,模板中需要填写内容 ...

  7. freemarker 生成word

    一.生成模板,动态获取的部分用${变量名},然后将word另存为xml文件,再将后缀名改成ftl格式.然后将模板放在对应的目录下. 二.引入freemarker包,mawen引用 <depend ...

  8. 我是如何使用freemarker生成Word文件的?

    推荐:亲身体验,数次踩坑,遂撰写此文,以备各位不时之需. 背景 一天,产品经理递给我了一份word报告,我定睛一看 这个文档有大大小小的标题层级,还有排版好的段落.各种一目了然的饼图.走势图,当然还少 ...

  9. FreeMarker生成word的代码

     用于生成word用的freemarker工具类 package com.ucap.netcheck.utils; import java.io.File; import java.io.File ...

  10. FreeMarker生成Word文档

    FreeMarker简介: FreeMarker是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页.电子邮件.配置文件.源代码等)的通用工具,它不是面向最终用户的,而是一个 ...

随机推荐

  1. Kubuesphere部署Ruoyi(三):持久化存储配置

    按照如下教程配置NFS 先服务器: https://kubesphere.io/zh/docs/v3.3/reference/storage-system-installation/nfs-serve ...

  2. 5219. 【GDOI2018模拟7.10】B

    5219. [GDOI2018模拟7.10]B 题目大意: 考试想法: 正解: 代码: 题目大意: 现在有一个字符串 s s s 当 s [ i ] s[i] s[i]为 I I I时 a n s [ ...

  3. ElementPlus 组件全局配置

    友链:语雀,在线文档协同平台 官方提供的全局配置:Config Provider 本文只做简单的模板参考,具体的配置请根据自己的业务灵活设置,如果你使用的是其它的ui框架,原理应该都差不多 入口文件的 ...

  4. Apktool-安卓逆向反编译工具的安装与使用

    一.安装Apktool 1.下载Windows 包装器脚本(右键单击,将连接另存为apktool.bat) 2.下载 Apktool-2,重命名为apktool.jar 3.将下载的放在同一文件夹下 ...

  5. Java代码读取properties配置文件

    读取properties配置文件 package com.easycrud.utils; import java.io.IOException; import java.io.InputStream; ...

  6. Prism Sample 3 自定义Region

    在例2中,我们使用了一个Region <ContentControl prism:RegionManager.RegionName="ContentRegion" /> ...

  7. Solon 统一的返回结果调整

    使用 "统一的渲染控制" 可以对输出做统一的控制外...还可以借助路由拦截器 RouterInterceptor ,对 mvc 返回结果做提交确认机制(即可修改)进行控制(相对来讲 ...

  8. Unity快速接入bugly, 支持Unity2021

    鹅厂提供的bugly官方demo工程打包后台也查不到日志,N年不更新(官方已经说不再维护),为此本人做了部分修改测试,提供一个快速接入工程的demo. Unity2021因为版本原因腾讯官方工程不能使 ...

  9. 2020-08-23:描述HTTPS和HTTP的区别。

    福哥答案2020-08-23: 1.地址区别http:http://开头.https:https://开头. 2.默认端口区别http:端口80.https:端口443. 3.数据传输区别http:明 ...

  10. 2021-03-08:在一个数组中,任何一个前面的数a,和任何一个后面的数b,如果(a,b)是降序的,就称为逆序对。返回逆序对个数。

    2021-03-08:在一个数组中,任何一个前面的数a,和任何一个后面的数b,如果(a,b)是降序的,就称为逆序对.返回逆序对个数. 福哥答案2021-03-08: 1.归并排序,从右往左,相等拷右. ...