poi-tl 生成 word 文件(插入文字、图片、表格、图表)
文章说明
本篇文章主要通过代码案例的方式,展示 poi-tl 生成 docx 文件的一些常用操作,主要涵盖以下内容 :
- 插入文本字符(含样式、超链接)
- 插入图片
- 插入表格
- 引入标签(通过可选文字的方式,这种方式也可以实现插入图片和插入表格)
当然 poi-tl 官方也有很详细的介绍,官网文档地址:https://deepoove.com/poi-tl/
项目初始化【必读】
项目创建好之后第一件事当然是引入依赖啦。
下面是 maven 引入的依赖,如果使用 Gradle 自行转成 Gradle 依赖。
<!-- poi 依赖 -->
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.5</version>
</dependency>
<!-- poi-tl -->
<dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.12.2</version>
</dependency>
在 poi-tl 生成 docx 文件时,先搞清楚三个问题:
- 定义 docx 模板文件:要生成怎么样的文件,自行创建一个 docx 的模板文件
- 定义模板文件的数据:向模板文件中,添加数据
- 生成文件位置:实际开发中大多会通过网络的方式传递,这里只展示生成在本地文件
本篇文章展示一些关键代码,为了减少冗余,我们可以定义一个生成 docx 的工具类 PoitlUtils:
import com.deepoove.poi.XWPFTemplate;import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;/*** @author 17279*/
public class PoitlUtils {/*** @param templateData 生成模板文件所需的所有数据* @param templateFilePath 模板文件路径(这里读取的是 resources 下的文件)* @param outputFilePath 模板文件输入的地址*/public static void generateWordFile(Map<String, Object> templateData, String templateFilePath, String outputFilePath) {// 读取模板文件try (InputStream templateIn = PoitlUtils.class.getResourceAsStream(templateFilePath)) {// 生成模板文件XWPFTemplate template = XWPFTemplate.compile(templateIn).render(templateData);template.writeAndClose(new FileOutputStream(outputFilePath));// 这个目的是:生成文件之后调用 cmd 打开本地文件,实际生产不需要该操作// Runtime.getRuntime().exec(String.format("cmd /c %s", outputFilePath));} catch (IOException e) {e.printStackTrace();}}
}
这里使用的所有 docs 模板文件,我都存放在 resources 的目录下。

插入文本
str_demo.docx 文件,文本使用 {{xxx}} 设置占位符:
{{str1}}
{{str2}}
{{str3}}
{{str4}}
{{?strArr1}}{{=#this}} {{/strArr1}}
案例代码:
// 模板文件
String templateFilePath = "/word_template/str_demo.docx";
// 输出文件
String outputFilePath = "D:/output.docx";
// 插入文本数据
Map<String, Object> templateData = new HashMap<String, Object>() {{// 直接插入 Stringput("str1", "直接插入 String");// 插入含有样式的文本put("str2", Texts.of("插入含有样式的文").color("ff0000").create());// 插入含超链接的文本put("str3", Texts.of("插入含有样式的文").link("http://www.shijialeya.top/").create());// 传入一个对象put("str4", Arrays.asList("键盘敲破", "工资过万"));// 遍历文本put("strArr1", Arrays.asList("派大星", "瘸老板", "海绵宝宝", "章鱼哥", "蟹老板"));
}};
// 文件生成
PoitlUtils.generateWordFile(templateData, templateFilePath, outputFilePath);
文档效果:

要注意 docx 文件上的 {{xxx}} xxx 的前后不要有空格,如果将外面的文字复制到 word 可能会自动加空格。
插入图片
img_demo.docx 文件,图片通过 {{@xxx}} 设置占位符:
{{@img1}}{{@img2}}{{@img3}}{{@img4}}{{?imgArr1}}{{@#this}} {{/imgArr1}}
案例代码:
// 模板文件
String templateFilePath = "/word_template/img_demo.docx";
// 输出文件
String outputFilePath = "D:/output.docx";
// 插入文本数据
Map<String, Object> templateData = new HashMap<String, Object>() {{// 直接插入本地图片(默认图片的宽度与文档的宽度一致)// put("img1", "C:/Users/17279/Pictures/head.jpg");// 插入本地图片,并设置图片大小put("img1", Pictures.ofLocal("C:/Users/17279/Pictures/head.jpg").size(100, 100).create());// 通过流的形式写入图片put("img2", Pictures.ofStream(new FileInputStream("C:/Users/17279/Pictures/head.jpg"), PictureType.JPEG).size(150, 150).create());// 写入网络图片put("img3", Pictures.ofUrl("http://file.shijialeya.top/head.jpg", PictureType.JPEG).size(170, 170).create());// 写入通过 Java 生成的图片put("img4", Pictures.ofBufferedImage(new BufferedImage(190, 190, BufferedImage.TYPE_BYTE_GRAY), PictureType.PNG).size(190, 190).create());// 遍历图片put("imgArr1", new ArrayList<Object>() {{add(Pictures.ofLocal("C:/Users/17279/Pictures/head.jpg").size(100, 100).create());add(Pictures.ofLocal("C:/Users/17279/Pictures/head.jpg").size(100, 100).create());add(Pictures.ofLocal("C:/Users/17279/Pictures/head.jpg").size(100, 100).create());}});
}};
// 文件生成
PoitlUtils.generateWordFile(templateData, templateFilePath, outputFilePath);
文档效果:

插入表格
tab_demo.docx 文件,图片通过 {{#xxx}} 设置占位符:
{{#tab1}}{{#tab2}}{{#tab3}}合同名称 {{tab4.contractName}}
合同时间 {{tab4.contractDate}} 合同金额 {{tab4.money}}
合同公司 {{tab4.company}}

案例代码:
// 模板文件
String templateFilePath = "/word_template/tab_demo.docx";
// 输出文件
String outputFilePath = "D:/output.docx";
// 插入文本数据
Map<String, Object> templateData = new HashMap<String, Object>() {{// 插入一个基础表格String[][] tabData1 = {new String[]{"姓名", "性别", "年龄"},new String[]{"派大星", "16", "男"},new String[]{"章鱼哥", "35", "男"}};put("tab1", Tables.of(tabData1).create());// 插入一个含有样式的表格Tables.TableBuilder tabData2 = Tables// 创建一个指定宽度的表格(docx 文档的 80% 宽度).ofPercentWidth("80%")// 表格设为水平居中.center()// 设置表格边框.border(BorderStyle.builder()// 边框样式.withType(XWPFTable.XWPFBorderType.DOUBLE)// 边框颜色.withColor("ff0000")// 边框粗细(边框为线条类型才会有效).withSize(12).build());tabData2.addRow(Rows.of("姓名", "性别", "年龄")// 设置文字颜色.textColor("FFFFFF")// 设置对应表格的背景颜色.bgColor("4472C4")// 文字居中.center().create());tabData2.addRow(Rows.of("派大星", "16", "男").create());put("tab2", tabData2.create());// 合并单元格String[][] tabData3 = {new String[]{"姓名", "性别", "年龄"},new String[]{"派大星", "16", "男"},new String[]{"章鱼哥", "35", "男"},new String[]{"共2人", null, null},};put("tab3", Tables.of(tabData3)// 添加单元格合并规则.mergeRule(MergeCellRule.builder()// [纵坐标, 横坐标] 索引从零开始,合并 [3, 0] 到 [3, 2] 位置的表格.map(MergeCellRule.Grid.of(3, 0), MergeCellRule.Grid.of(3, 2)).build()).create());// 对应格式一定的表格,直接采用字符串替换即可put("tab4", new HashMap<String, Object>() {{put("contractName", "第一季度财务报告");put("contractDate", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));put("company", "xxx有限责任公司");put("money", 10089.33);}});
}};
// 文件生成
PoitlUtils.generateWordFile(templateData, templateFilePath, outputFilePath);
文档效果:
引入标签
插入图片
上面也有插入图片的方式,但是通过引入标签的方式插入图片时,可以先在 word 模板文件中提前编辑好图片的样式,通过替换图片的方式,会保留原本设置好的样式。
在 docx 模板文件中先插入一张图片,并且调整好图片的样式,之后右键图片选择【查看可选文字】,在可选文字中通过 {{xxx}} 的方式填写属性名称。
【特别提醒】貌似 WPS 没有可选文字的功能,不确定是不是 WPS 版本的原因,反正我没找到可选文字。
因此我特地把 WPS 卸载之后换成了 Office 工具。

案例代码:
// 模板文件
String templateFilePath = "/word_template/quote_demo01.docx";
// 输出文件
String outputFilePath = "D:/output.docx";
// 插入文本数据
Map<String, Object> templateData = new HashMap<String, Object>() {{// 直接插入本地图片,这里会保留模板文件的图片样式put("label1", Pictures.ofLocal("C:/Users/17279/Pictures/head.jpg").create());
}};
// 文件生成
PoitlUtils.generateWordFile(templateData, templateFilePath, outputFilePath);
文档效果:

插入单系列图表
单系列图表指的是饼图(3D饼图)、圆环图等。
同引入标签插入图片一样,在插入图表的时候,需要在 docx 模板中创建一个单系列的图表,设置好样式,之后右键图表选择【查看可选文字】,在可选文字中通过 {{xxx}} 的方式填写属性名称。

案例代码:
// 模板文件
String templateFilePath = "/word_template/quote_demo02.docx";
// 输出文件
String outputFilePath = "D:/output.docx";
// 插入文本数据
Map<String, Object> templateData = new HashMap<String, Object>() {{// 添加单系列图表的表格数据put("label2", Charts.ofSingleSeries("商品类型", new String[]{"电器类", "数码类", "生活用品类", "食品类", "其他"}).series("数量", new Integer[]{30, 8, 25, 11, 3}).create());
}};
// 文件生成
PoitlUtils.generateWordFile(templateData, templateFilePath, outputFilePath);
文档效果:

插入多系列图表
多系列图表指的是条形图(3D条形图)、柱形图(3D柱形图)、面积图(3D面积图)、折线图(3D折线图)、雷达图、散点图等。
模板文件如下:

案例代码:
// 模板文件
String templateFilePath = "/word_template/quote_demo03.docx";
// 输出文件
String outputFilePath = "D:/output.docx";
// 插入文本数据
Map<String, Object> templateData = new HashMap<String, Object>() {{// 添加单系列图表的表格数据put("label3", Charts.ofMultiSeries("销售额", new String[]{"第一季度", "第二季度", "第三季度", "第四季度"}).addSeries("电器类", new Integer[]{22, 25, 28, 25}).addSeries("数码类", new Integer[]{5, 10, 8, 4}).addSeries("其他", new Integer[]{30, 42, 22, 33}).create());
}};
// 文件生成
PoitlUtils.generateWordFile(templateData, templateFilePath, outputFilePath);
文档效果:
插入组合图表
组合图表指的是由多系列图表(柱形图、折线图、面积图)组合而成的图表。
模板文件如下:

案例代码:
// 模板文件
String templateFilePath = "/word_template/quote_demo04.docx";
// 输出文件
String outputFilePath = "D:/output.docx";
// 插入文本数据
Map<String, Object> templateData = new HashMap<String, Object>() {{// 添加单系列图表的表格数据put("label4", Charts.ofComboSeries("汽车销售额", new String[]{"第一季度", "第二季度", "第三季度", "第四季度"})// 添加柱状图数据.addBarSeries("比亚迪", new Double[]{12.3, 11.5, 9.7, 12.0}).addBarSeries("广汽", new Double[]{6.2, 5.8, 5.7, 6.6}).addBarSeries("小米", new Double[]{0.0, 0.0, 10.2, 11.2})// 添加折线图数据.addLineSeries("国内均值", new Double[]{10.0, 12.2, 11.2, 9.8}).addLineSeries("全球均值", new Double[]{8.3, 10.2, 10.0, 8.8}).create());
}};
// 文件生成
PoitlUtils.generateWordFile(templateData, templateFilePath, outputFilePath);
文档效果:

相关文章:
poi-tl 生成 word 文件(插入文字、图片、表格、图表)
文章说明 本篇文章主要通过代码案例的方式,展示 poi-tl 生成 docx 文件的一些常用操作,主要涵盖以下内容 : 插入文本字符(含样式、超链接)插入图片插入表格引入标签(通过可选文字的方式,这种方…...
centos上部署Ollama平台,实现语言大模型本地部署
网上有很多大模型,很多都是远程在线调用ChatGPT的api来实现的,自己本地是没有大模型的,这里和大家分享一个大模型平台,可以实现本地快速部署大模型。 Ollama是一个开源项目,它提供了一个平台和工具集,用于部…...
Java学习 - Redis Redigo简单介绍
Redigo 驱动下载 go get github.com/garyburd/redigo/redis获取redis服务器连接 c, err : redis.Dial("tcp", "127.0.0.1:6379")if err ! nil {panic(err) }defer c.Close()命令使用 v, err : c.Do("SET","hello","world&quo…...
【鸿蒙学习笔记】ArkTS组件 Blank
官方文档:Blank 目录标题...
如何使用Spring Boot进行单元测试
如何使用Spring Boot进行单元测试 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨如何在Spring Boot项目中进行单元测试,确保代码质量…...
2024steam夏促商店打不开、steam活动加载不了解决方法一览
今年的夏促终于开始了!目前可以看到很多精品小游戏在促销列表内,活动正式开启后还不知道又会是怎样的一幅场景。因为每年夏促都会有不少刚高考完的新手加入,遇到常见的steam商店打不开、活动页面不加载等问题不知道怎么解决。所以这里给大家准备了几种常…...
IPC进程通信:QNX
引言 在现代操作系统中,进程间通信(IPC)机制是实现进程间数据交换和同步的关键技术。IPC允许多个进程共享信息和资源,从而协同工作完成复杂任务。在QNX Neutrino系统中,IPC尤为重要,因为QNX主要面向实时系…...
OpenCV学习之cv2.imshow()函数
OpenCV学习之cv2.imshow()函数 一、简介 cv2.imshow 是 OpenCV 库中用于显示图像的基本函数之一。在图像处理和计算机视觉的过程中,使用该函数可以快速预览处理后的图像,便于调试和结果展示。 二、基本语法 cv2.imshow(WindowName, Imgmat)三、参数说…...
Oracle、MySQL、PostGreSQL、SQL Server-空值
Oracle、MySQL、PostGreSQL、SQL Server-null value 最近几年数据库市场百花齐放,在做跨数据库迁移的数据库选型时,除了性能、稳定、安全、运维、功能、可扩展外,像开发中对于值的处理往往容易被人忽视, 之前写过一篇关于PG区别O…...
python pip详解1
一、简介 pip是python的一个软件包管理工具,同yum,apt作用一致,pip有两种使用方式:pip模块和pip命令,示例如下: python -m pip install package pip install package二、命令行详解 python -m pip --hel…...
Linux常用命令大全(超详细!!!)
文章目录 1.Linux是什么1.1 关于Linux我们主要学习什么1.1 学习Linux常见命令的前置知识 2. Linux常见命令2.1 ls命令2.2 cd命令2.3 pwd命令2.4 touch命令2.5 cat命令2.6 echo命令2.7 vim命令2.8 mkdir 命令2.9 rm命令2.10 cp命令2.11 mv命令2.12 grep命令2.13 ps命令2.14 nets…...
TDD测试驱动开发
为什么需要TDD? 传统开发方式,带来大量的低质量代码,而代码质量带来的问题: 1.在缺陷的泥潭中挣扎 开发长时间投入在缺陷的修复中,修复完依赖测试做长时间的回归测试 2.维护困难,开发缓慢 比如重复代码&am…...
huggingface镜像站
huggingface下载太慢,大模型文件太大。用huggingface_hub镜像。 pip install -U huggingface_hub pip install huggingface-cli export HF_ENDPOINThttps://hf-mirror.com huggingface-cli download --resume-download shenzhi-wang/Llama3-8B-Chinese-Chat --loc…...
Java中如何实现数据库连接池优化?
Java中如何实现数据库连接池优化? 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将深入探讨在Java应用程序中如何实现数据库连接池优化&am…...
002 SpringMVC入门项目搭建
文章目录 HelloController.javaspringmvc.xmlweb.xmlpom.xmlhello.jsp http://localhost:8080/showView http://localhost:8080/showData HelloController.java package com.springmvc.controller;import org.springframework.stereotype.Controller; import org.springframewo…...
为什么要使用多线程(并发编程)
目录 1.上下文的切换 1.1 什么是上下文切换 2. 并发编程的死锁问题 2.1 死锁产生的原因 2.2 避免死锁的方法 3.资源限制的挑战3.1 什么是资源限制 并发编程的目的是为了让程序更快,大家都知道并不是开启的线程越多越快,因为开启的线程越多随即面临…...
Unity编辑器工具---版本控制与自动化打包工具
Unity - 特殊文件夹【作用与是否会被打包到build中】 Unity编辑器工具—版本控制与自动化打包工具: 面板显示:工具包含一个面板,用于展示软件的不同版本信息。版本信息:面板上显示主版本号、当前版本号和子版本号。版本控制功能…...
amis-editor 注册自定义组件
建议先将amis文档从头到尾,仔细看一遍。 参考:amis - 低代码前端框架 amis 的渲染过程是将 json 转成对应的 React 组件。先通过 json 的 type 找到对应的 Component,然后把其他属性作为 props 传递过去完成渲染。 import * as React from …...
(上位机APP开发)调用华为云命令API接口给设备下发命令
一、功能说明 通过调用华为云IOT提供的命令下发API接口,实现下面界面上相同的功能。调用API接口给设备下发命令。 二、JavaScript代码 function sendUnlockCommand() {var requestUrl = "https://9bcf4cfd30.st1.iotda-app.cn-north-4.myhuaweicloud.com:443/v5/iot/60…...
排序算法系列一:选择排序、插入排序 与 希尔排序
目录 零、说在前面 一、理论部分 1.1:选择排序 1.1.1:算法解读: 1.1.2:时间复杂度 1.1.3:优缺点: 1.1.4:代码: 1.2:插入排序 1.2.1:算法解读&#x…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
