当前位置: 首页 > news >正文

【Java】使用FreeMarker来实现Word自定义导出

前言

在对一些特定导出功能,使用常规Excel无法解决的,通常使用Word来实现导出功能,这篇介绍下如何在Java中使用FreeMarker模板注入方式来实现Word导出功能

导出案例(已作打码处理)

在这里插入图片描述
或者:
在这里插入图片描述

准备工作

第一步:maven依赖库

		<!-- freemarker (用于Word导出)--><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>${freemarker.version}</version></dependency>

第二步:Word导出工具类


import com.zrxt.common.config.RuoYiConfig;
import com.zrxt.common.core.text.CharsetKit;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;/*** @ClassName WordUtil* @Description 使用Freemarker生成Word文档工具类* @Author* @Date 2023/12/14**/
public class WordUtil {/*** 使用Freemarker自动生成Word文档(磁盘路径方法)** @param dataMap      保存Word文档中所需要的数据* @param templatePath 模板文件的绝对路径* @param templateFile 模板文件的名称* @throws Exception*/public static void CreateWord(HttpServletResponse response, Map<String, Object> dataMap, String templatePath, String templateFile) throws Exception {Writer out = null;try {// 设置FreeMarker的版本Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);// 设置Freemarker的编码格式configuration.setDefaultEncoding(CharsetKit.UTF_8);// 设置FreeMarker生成Word文档所需要的模板的路径configuration.setDirectoryForTemplateLoading(new File(templatePath));// 设置FreeMarker生成Word文档所需要的模板名称Template t = configuration.getTemplate(templateFile, CharsetKit.UTF_8);// 创建一个Word文档的输出流out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));//FreeMarker使用Word模板和数据生成Word文档t.process(dataMap, out);}catch (Exception e){e.printStackTrace();}finally {assert out != null;out.flush();out.close();}}/*** 使用Freemarker自动生成Word文档** @param dataMap      保存Word文档中所需要的数据* @param templatePath 模板文件的路径(绝对)* @param templateFile 模板文件的名称* @throws Exception*/public static void GeneratorWord(HttpServletResponse response, Map<String, Object> dataMap, String templatePath, String templateFile) throws Exception {Writer out = null;try {// 设置FreeMarker的版本Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);// 设置Freemarker的编码格式configuration.setDefaultEncoding(CharsetKit.UTF_8);//相对路径加载模板方法configuration.setTemplateLoader(new ClassTemplateLoader(WordUtil.class,templatePath));// 设置FreeMarker生成Word文档所需要的模板名称Template template  = configuration.getTemplate(templateFile, CharsetKit.UTF_8);// 创建一个Word文档的输出流out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));//FreeMarker使用Word模板和数据生成Word文档template .process(dataMap, out);}catch (Exception e){e.printStackTrace();}finally {assert out != null;out.flush();out.close();}}/*** 下载文件** @param path     文件的位置* @param fileName 自定义下载文件的名称* @param response http响应* @param request  http请求*/public static void downloadFile(String path, String fileName, HttpServletResponse response, HttpServletRequest request) {try {File file = new File(path);// 中文乱码解决String type = request.getHeader("User-Agent").toLowerCase();if (type.indexOf("firefox") > 0 || type.indexOf("chrome") > 0) {// 谷歌或火狐fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), "iso8859-1");} else {// IEfileName = URLEncoder.encode(fileName, CharsetKit.UTF_8);}// 设置响应的头部信息response.setHeader("content-disposition", "attachment;filename=" + fileName);// 设置响应内容的类型response.setContentType(getFileContentType(fileName) + "; charset=" + CharsetKit.UTF_8);// 设置响应内容的长度response.setContentLength((int) file.length());// 输出outStream(new FileInputStream(file), response.getOutputStream());} catch (Exception e) {e.printStackTrace();}}/*** 文件的内容类型*/private static String getFileContentType(String name) {String result = "";String fileType = name.toLowerCase();if (fileType.endsWith(".png")) {result = "image/png";} else if (fileType.endsWith(".gif")) {result = "image/gif";} else if (fileType.endsWith(".jpg") || fileType.endsWith(".jpeg")) {result = "image/jpeg";} else if (fileType.endsWith(".svg")) {result = "image/svg+xml";} else if (fileType.endsWith(".doc")) {result = "application/msword";} else if (fileType.endsWith(".xls")) {result = "application/x-excel";} else if (fileType.endsWith(".zip")) {result = "application/zip";} else if (fileType.endsWith(".pdf")) {result = "application/pdf";} else {result = "application/octet-stream";}return result;}/*** 基础字节数组输出*/private static void outStream(InputStream is, OutputStream os) {try {byte[] buffer = new byte[10240];int length = -1;while ((length = is.read(buffer)) != -1) {os.write(buffer, 0, length);os.flush();}} catch (Exception e) {e.printStackTrace();} finally {try {os.close();is.close();} catch (IOException e) {e.printStackTrace();}}}/*** 检查存储生成文件的路径是否存在,如果不存在则新建路径.** @param directory the directory name, like '\dir-name'*/public static void CheckDownloadPath(String directory) {File path = new File(RuoYiConfig.getDownloadPath() + directory);if (!path.exists()) {path.mkdirs();}}
}

模板准备

第一步:首先要编写Word模板,可以参考文章开头的示例图片。
第二步:Word模板编写好后,点击另存为,然后选择Word 2003 XML文档
在这里插入图片描述
第三步:保存好后的文件打开,Ctrl+A复制文档所有内容,然后在线搜索XML格式化在线工具,我这边提供一个现成的XML格式化在线工具,将内容全部粘贴进去后点击格式化按钮。
在这里插入图片描述

创建ftl文件

上面所有工作准备好后,就可以在resource目录下创建一个.ftl文件,然后将格式化后的代码复制到文件中即可。
在这里插入图片描述

测试导出是否完整

Controller层编写测试代码,判断是否可以正常导出

    /*** 导出(word)关键过程控制详细信息详细信息(xml版本)*/@PostMapping(value = "/exportWord")public void getInfo(MakeCriticalProcessControl param, HttpServletResponse response) {try {MakeCriticalProcessControl obj=makeCriticalProcessControlService.selectById(param.getId());Map<String, Object> context = new HashMap<>();context.put("obj", obj);WordUtil.GeneratorWord(response, context, "/wordDocumentFtl/make/", "MakeCriticalProcessControl.ftl");} catch (Exception e) {e.printStackTrace();throw new ServiceException("导出Word文件失败,请稍后重试!");}}

WordUtil.GeneratorWord()方法
第二个参数context为数据库的数据,要往word中空白单元格所填充的。
第三个参数为templatePath – 模板文件的路径(绝对)
第四个参数为具体的模板的文件名称(带后缀)

注:测试后需检查格式是否混乱,是否有缺少单元格等情况,如果有则检查原Word模板并修复然后重新走一遍流程即可

如何将数据库数据填充到Word中

ftl文件中的内容是一行一行的顺序,也就是从第一行的第一个单元格开始,然后是第一行第二个单元格,第三个单元格…
然后第一行如果结束了,则是第二行第一个单元格、第二个单元格、第三个…

以此类推,所以优先找到对应标题的对应数据单元格在ftl文件的哪个位置。

如果是单个属性或对象类型的,可以在controller中使用Map<String, Object> context = new HashMap<>();将数据put,然后key作为键,在ftl文件中可以把属性获取出来。例如下图
在这里插入图片描述
在这里插入图片描述

如果涉及到遍历,将一个list结果导出到word,请看以下案例

如果是遍历导出的话,则在word模板设置时,只需要设置一行表头,下方对应一行空的单元格即可,我们要循环空的单元格,然后将数据挨个写入。
在这里插入图片描述
只需要在标题下写一行即可

在controller中依旧是查询出对应list,然后put到map中
在这里插入图片描述
图文解释如下
1:判断list是否为空
2:循环遍历list
3:一般<w:tr>表示为行,所以这里的意思就是,list有多少数据,则就生成多少行
如何要获取值的话就可以${item.属性名!}
在这里插入图片描述

如果涉及到If语句或者是需要将数字转为对应的状态

在这里插入图片描述

http://www.dtcms.com/a/315017.html

相关文章:

  • 反转字符串中的元音字母:Swift 双指针一步到位
  • EXPLAIN工具:查询执行计划分析与索引诊断
  • 【数据结构】排序(sort) -- 插入排序
  • 如何设置端口映射?防火墙/路由器/纯软件工具多种常用方案步骤,确保任意内网ip端口映射公网访问到
  • 《汇编语言:基于X86处理器》第11章 复习题和练习
  • RocketMQ与Kafka 消费者组的‌重平衡操作消息顺序性对比
  • Hadoop MapReduce 3.3.4 讲解~
  • Linux系统编程-文件操作(黑马笔记)
  • 基于Springboot+Mybatis+thymeleaf的个人博客系统的设计与实现
  • EXCEL删除数据透视表
  • 洛谷 P3373 【模板】线段树 2- 普及+/提高
  • C# 类型
  • 基于PSO-NSGAIII混合优化的生产调度算法matlab仿真,输出甘特图,对比PSO和NSGAIII
  • traefik网关鉴权中间件转发multipart/form-data请求的multipart: NextPart: EOF问题
  • 09 Linux基础(8.4)
  • (一)vue3项目初始化(create-vue)
  • 构建属于自己的第一个 MCP 服务器:初学者教程
  • web:ts的字符串string和String
  • Flutter简单讲解
  • ctfshow:pwn85(高级ROP 64 位 Partial-RELRO)、pwn141
  • 内网应用如何实现外网访问?常见方案和简单便捷通用方法步骤
  • SpringBoot格式化数据库表格字段时间戳
  • 华莱士“武”动新章:武林外传IP赋能,开启品牌破圈之旅!
  • XXE漏洞原理及利用
  • VBA-Excel图片下载到本地文件夹
  • 机器学习05——正则化与逻辑回归
  • 大模型LLM介绍
  • 《 java 随想录》| LeetCode二叉树高频算法题
  • WPS2025(官方版)下载与安装教程
  • 一命速通Docker安装+镜像源配置