使用 POI-TL 和 JFreeChart 动态生成 Word 报告
文章目录
- 前言
- 一、需求背景
- 二、方案分析
- 三、 POI-TL + JFreeChart 实现
- 3.1 Maven 依赖
- 3.3 word模板设置
- 3.2 实现代码
- 踩坑
前言
在开发过程中,我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart,实现动态生成 Word 报告的功能,并分享一些实际开发中的踩坑经验。
word生成方案:
- freemarker+ftl
- pot-tl模板替换
- poi硬编码
一、需求背景
在之前的文章中,我们已经介绍了如何使用模板替换、复杂表格和图片插入等功能。此次的需求是生成一个包含统计图的 Word 报告,统计图需要根据动态数据生成。面临的主要问题包括:
- 选择 Word 生成方案:如何在 Word 中动态插入数据和图表?
- 图片插入方案:如何将生成的统计图插入到 Word 中?
- 生成统计图表方案:如何根据数据动态生成统计图?
二、方案分析
-
POI 硬编码
直接使用 Apache POI 硬编码生成 Word 文档,虽然可行,但代码复杂且难以维护,因此不推荐。 -
FreeMarker + FTL
FreeMarker 可以实现文本替换和图片插入,理论上符合需求。但 FTL 模板的维护较为繁琐,尤其是在处理复杂表格和图片时。 -
POI-TL + JFreeChart
POI-TL 是一个基于 Apache POI 的模板引擎,支持文本替换、图片插入等功能。结合 JFreeChart 生成统计图,可以很好地满足需求。
三、 POI-TL + JFreeChart 实现
关于JFreeChart请移步使用 JFreeChart 创建动态图表:从入门到实战
3.1 Maven 依赖
首先,需要在项目中引入 POI-TL 和 JFreeChart 的依赖。注意 POI 和 POI-TL 的版本需要对应,否则可能会出现 NoSuchMethod 等错误。
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-excelant</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.jfree</groupId><artifactId>jfreechart</artifactId><version>1.5.3</version></dependency><dependency><groupId>com.deepoove</groupId><artifactId>poi-tl</artifactId><version>1.10.0</version></dependency>
3.3 word模板设置
在 Word 模板中,使用占位符标记需要替换的内容。对于图片,需要在占位符前加 @,例如:
- 替换文本:时间 -> ${date}
- 插入图片:${@dailyOnlinePic}
- 条件判断:${?isTrue} 条件内容 ${/isTrue}
3.2 实现代码
以下是核心实现代码:
private static final String TEMPLATE_PATH = "classpath:template/report.docx";private static final String OUTPUT_DIR = "D:/data/upload/analysis/";private static final String PIC_DIR = OUTPUT_DIR + "pic/";public void getWord(String curDistCode) {try {// 获取模板文件File file = ResourceUtils.getFile(TEMPLATE_PATH);// 构建模板替换的数据Map<String, Object> dataMap = buildTemplateData(curDistCode);// 生成最终文件路径String fileName = UUIDUtil.genUUID32() + ".docx";String filePath = OUTPUT_DIR + fileName;// 使用 POI-TL 渲染模板并保存try (XWPFTemplate template = XWPFTemplate.compile(file, Configure.newBuilder().buildGramer("${", "}").build()).render(dataMap)) {template.writeToFile(filePath);}//上传文件返回附件id} catch (Exception e) {e.printStackTrace();}}private Map<String, Object> buildTemplateData(String curDistCode) throws IOException {Map<String, Object> dataMap = new HashMap<>();// 设置日期和在线总数dataMap.put("date", LocalDate.now());// 查询设备数据dataMap.put("onlineTotal", 100);// 生成每日在线图表DefaultCategoryDataset dailyData = buildDailyDataset(stationByTime);String dailyPicFile = generateChart(dailyData, "监测站总在线数", "小时", "数量", dailyOnlineTxtEnum);dataMap.put("dailyOnlinePic", Pictures.ofStream(new FileInputStream(dailyPicFile), PictureType.JPEG).size(600, 200).create());return dataMap;}// 构建每日在线图表的数据集private DefaultCategoryDataset buildDailyDataset(List<FireStationByTimeDTO> stationByTime) {DefaultCategoryDataset dataset = new DefaultCategoryDataset();stationByTime.forEach(t -> dataset.addValue(t.getOnlineNum(), "", t.getTime()));return dataset;}// 生成图表并保存为图片private String generateChart(DefaultCategoryDataset dataset, String title, String xAxisLabel, String yAxisLabel, DailyOnlineTxtEnum style) throws IOException {// 设置全局字体(支持中文)StandardChartTheme chartTheme = new StandardChartTheme("CN");chartTheme.setExtraLargeFont(new Font("宋体", Font.PLAIN, 14)); // 标题字体chartTheme.setLargeFont(new Font("宋体", Font.PLAIN, 14)); // 图例字体chartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 12)); // 轴标签字体ChartFactory.setChartTheme(chartTheme);// 创建图表JFreeChart chart = ChartFactory.createLineChart(title, xAxisLabel, yAxisLabel, dataset);setChartStyle(chart, style);// 保存图表为图片String picFile = PIC_DIR + UUIDUtil.genUUID32() + ".png";//int numberOfCategories = dataset.getColumnCount();//int width = Math.max(800, numberOfCategories * 50); // 每个类别宽度为50,最小宽度为800ChartUtils.saveChartAsPNG(new File(picFile), chart, 1200, 400);return picFile;}// 设置图表样式private void setChartStyle(JFreeChart chart) {CategoryPlot plot = chart.getCategoryPlot();chart.setBackgroundPaint(Color.WHITE);plot.setBackgroundPaint(Color.WHITE);plot.setDomainGridlinePaint(Color.LIGHT_GRAY);plot.setRangeGridlinePaint(Color.LIGHT_GRAY);// 设置第一条折线的粗细plot.getRenderer().setSeriesStroke(0, new BasicStroke(5.0f));// 根据样式设置折线颜色plot.getRenderer().setSeriesPaint(0, Color.RED);}
效果
踩坑
- 插入图片如何占位
与常规文本替换不同,图片插入需要在占位符前加 @,例如${@dailyOnlinePic}
或{{@pic}}
- 统计图中文乱码
JFreeChart 默认不支持中文,需要通过设置全局字体解决:
// 设置全局字体(支持中文)StandardChartTheme chartTheme = new StandardChartTheme("CN");chartTheme.setExtraLargeFont(new Font("宋体", Font.PLAIN, 14)); // 标题字体chartTheme.setLargeFont(new Font("宋体", Font.PLAIN, 14)); // 图例字体chartTheme.setRegularFont(new Font("宋体", Font.PLAIN, 12)); // 轴标签字体ChartFactory.setChartTheme(chartTheme);
3.统计图横坐标…
如果生成的图片宽度不够,横坐标可能会显示不全。可以通过增加图片宽度解决,插入时等比例缩放:
ChartUtils.saveChartAsPNG(new File(picFile), chart, 1200, 400);
dataMap.put("dailyOnlinePic",
Pictures.ofStream(new FileInputStream(dailyPicFile), PictureType.JPEG).size(600, 200).create());
或动态计算需要生成的图片宽度:
int numberOfCategories = dataset.getColumnCount();
int width = Math.max(800, numberOfCategories * 50); // 每个类别宽度为50,最小宽度为800
- 模板条件判断
POI-TL支持复杂的条件判断,例如结合逻辑运算符(&&、||)和比较运算符(==、!=、>、< 等)。
例如:
{{?isStudent}}{{?age > 18}}您是一名成年学生。{{/age > 18}}
{{/isStudent}}
此次实验boolean好用,字符串==匹配失败。
相关文章:

使用 POI-TL 和 JFreeChart 动态生成 Word 报告
文章目录 前言一、需求背景二、方案分析三、 POI-TL JFreeChart 实现3.1 Maven 依赖3.3 word模板设置3.2 实现代码 踩坑 前言 在开发过程中,我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart,实现动态生成 W…...

xxl-job的分片广播
目录 xxl-job的分片广播 场景引入 xxl-job简介 xxl-job的部署安装 代码编写 1.导入依赖 2.yml文件编写 3.编写xxl-job执行器配置类,维护一个xxl-job执行器的bean 4.编写第一个任务,任务名字叫firstJob 5.进入服务端,增加执行器和任务…...

MobaXterm破解会话上限限制
1. 下载安装包MobaXterm-Keygen 下载路径: https://gitcode.com/gh_mirrors/mob/MobaXterm-Keygen 2. 搭建python3环境 window下python3环境搭建可参考网站: https://blog.csdn.net/enteracity/article/details/135479689 3. 生成文件Custom.mxtpro…...

vscode设置保存时自动缩进和格式化
参考博客 如何在 VSCode 中自动缩进你的代码 | Linux 中国 省流 使用 Ctrl Shift P 来打开命令模式,搜索 Open User Settings 并按下回车你需要搜索 Auto Indent,并在 “编辑器:自动缩进(Editor: Auto Indent)” 中选择 “全部(Full)”P…...

一键查看电脑各硬件详细信息 轻松查看电脑硬件参数
今天为大家推荐两款非常实用的电脑硬件查看软件,它们能够一键快速查看电脑的各种配置信息,使用起来非常方便。 一键查看电脑各硬件详细信息 这款软件是绿色版的,无需安装,打开即可使用,文件大小仅为900多KB࿰…...

【C++11】lambda和包装器
1.新的类功能 1.1默认的移动构造和移动赋值 原来C类中,有6个默认成员函数:构造函数/析构函数/拷⻉构造函数/拷⻉赋值重载/取地址重 载/const 取地址重载,最后重要的是前4个,后两个⽤处不⼤,默认成员函数就是我们不写…...

react redux用法学习
参考资料: https://www.bilibili.com/video/BV1ZB4y1Z7o8 https://cn.redux.js.org/tutorials/essentials/part-5-async-logic AI工具:deepseek,通义灵码 安装相关依赖: 使用redux的中间件: npm i react-reduxreact-…...

前端HTML标签 meta中常见的一些属性
meta中常见的一些属性 <meta/> 标签的属性 <meta/> 是什么? <meta/> 标签主要用于表示和当前文档相关的 元数据 信息。 而 元数据(metadata),简单的来说就是描述数据的数据。例如,一个 HTML 文件是一…...

127,【3】 buuctf [NPUCTF2020]ReadlezPHP
进入靶场 吓我一跳 查看源码 点击 审计 <?php// 定义一个名为 HelloPhp 的类,该类可能用于执行与日期格式化相关的操作 class HelloPhp {// 定义一个公共属性 $a,用于存储日期格式化的模板public $a;// 定义一个公共属性 $b,用于存储…...

继承(python)
一、基础知识 (一)定义:子类能继承父类所有的公有属性和公有方法(先使用子类的方法、属性) (二)格式: class 子类名(父类名): #父类 class Ph…...

驱动开发系列36 - Linux Graphics 2D 绘制流程
一: 概述 在Linux中,2D绘制流程是操作系统、图形库、显示协议、驱动程序等多个组件协调工作的结果。整体流程如下步骤所示: 1. 客户端请求:客户端程序(如GTK、Qt应用程序)通过X11协议与Xorg-Server通信(或通过Wayland协议与Wayland合成器通信)、请求绘制2D图形,比如绘制…...

STL函数算法笔记
STL函数算法笔记 今天我们来学习的是STL库中的一些函数。首先,STL这个东西大家一定非常熟悉,里面很多的数据结构都帮了大家不少忙,那么今天我们就来说几个重要的数据结构。 向量 向量,也就是数据结构vector,你也可以称之为动态数组,本质跟数组差不多,只不过有一些好处…...

【Vue】在Vue3中使用Echarts的示例 两种方法
文章目录 方法一template渲染部分js部分方法一实现效果 方法二template部分js or ts部分方法二实现效果 贴个地址~ Apache ECharts官网地址 Apache ECharts示例地址 官网有的时候示例显示不出来,属于正常现象,多进几次就行 开始使用前,记得先…...

小红书自动化:如何利用Make批量生成爆款笔记
小红书自动化:如何利用Make制作个人自媒体中心,批量生成爆款笔记 引言 在如今信息爆炸的时代,如何高效地获取和分享优质内容,成为了每位自媒体工作者必须面对的挑战。你是否想过,如果能够将这项繁复的工作实现自动化…...

学习率调整策略 | PyTorch 深度学习实战
前一篇文章,深度学习里面的而优化函数 Adam,SGD,动量法,AdaGrad 等 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started 本篇文章内容来自于 强化学习必修课:引…...

DeepSeekMoE 论文解读:混合专家架构的效能革新者
论文链接:DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models 目录 一、引言二、背景知识(一)MoE架构概述(二)现有MoE架构的问题 三、DeepSeekMoE架构详解(一&a…...

以下是基于巨控GRM241Q-4I4D4QHE模块的液位远程控制系统技术方案:
以下是基于巨控GRM241Q-4I4D4QHE模块的液位远程控制系统技术方案: 一、系统概述 本系统采用双巨控GRM241Q模块构建4G无线物联网络,实现山上液位数据实时传输至山下水泵站,通过预设逻辑自动控制水泵启停,同时支持APP远程监控及人工…...

【JVM详解五】JVM性能调优
示例: 配置JVM参数运行 #前台运行 java -XX:MetaspaceSize-128m -XX:MaxMetaspaceSize-128m -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio8 - XX:UseConcMarkSweepGC -jar /jar包路径 #后台运行 nohup java -XX:MetaspaceSize-128m -XX:MaxMetaspaceS…...

2.10日学习总结
题目一: AC代码 #include <stdio.h>#define N 1000000typedef long long l;int main() {int n, m;l s 0;l a[N 1], b[N 1];int i 1, j 1;scanf("%d %d", &n, &m);for (int k 1; k < n; k) {scanf("%lld", &a[k]);…...

疯狂前端面试题(四)
一、Ajax、JSONP、JSON、Fetch 和 Axios 技术详解 1. Ajax(异步 JavaScript 和 XML) 什么是 Ajax? Ajax 是一种用于在不刷新页面的情况下与服务器进行数据交互的技术。它通过 XMLHttpRequest 对象实现。 优点 - 支持同步和异步请求。 - 能…...

YOLOv11-ultralytics-8.3.67部分代码阅读笔记-metrics.py
metrics.py ultralytics\utils\metrics.py 目录 metrics.py 1.所需的库和模块 2.def bbox_ioa(box1, box2, iouFalse, eps1e-7): 3.def box_iou(box1, box2, eps1e-7): 4.def bbox_iou(box1, box2, xywhTrue, GIoUFalse, DIoUFalse, CIoUFalse, eps1e-7): 5.def mas…...

SuperCopy解除网页禁用复制功能插件安装和使用
点击下载《SuperCopy解除网页禁用复制功能插件》 1. 前言 在当今数字化时代,网络已成为我们获取信息和知识的主要渠道。互联网如同一片浩瀚无垠的知识海洋,蕴藏着无数的资源,从学术论文到生活小窍门,从专业教程到娱乐资讯&#…...

UP-VLA:具身智体的统一理解与预测模型
25年1月来自清华大学和上海姚期智研究院的论文“UP-VLA: A Unified Understanding and Prediction Model for Embodied Agent”。 视觉-语言-动作 (VLA) 模型的最新进展,利用预训练的视觉语言模型 (VLM) 来提高泛化能力。VLM 通常经过视觉语言理解任务的预训练&…...

Unity 基于状态机的逻辑控制详解
状态机是游戏开发中常用的逻辑控制方法,它可以将复杂的逻辑分解成多个独立的状态,并通过状态转移来控制逻辑的执行流程。本文将详细介绍如何在 Unity 中基于状态机实现逻辑控制,并提供技术详解和代码实现。 一、状态机简介 1.1 基本概念 状…...

傅里叶单像素成像技术研究进展
摘要:计算光学成像,通过光学系统和信号处理的有机结合与联合优化实现特定成像特性的成像系统,摆脱了传统成像系统的限制,为光学成像技术添加了浓墨重彩的一笔,并逐步向简单化与智能化的方向发展。单像素成像(Single-Pi…...

IDEA接入DeepSeek
IDEA 目前有多个途径可以接入deepseek,比如CodeGPT或者Continue,这里借助CodeGPT插件接入,CodeGPT目前用的人最多,相对更稳定 一、安装 1.安装CodeGPT idea插件市场找到CodeGPT并安装 2.创建API Key 进入deepseek官网…...

前端如何判断浏览器 AdBlock/AdBlock Plus(最新版)广告屏蔽插件已开启拦截
2个月前AdBlock/AdBlock Plus疑似升级了一次 因为自己主要负责面对海外的用户项目,发现以前的检测AdBlock/AdBlock Plus开启状态方法已失效了,于是专门研究了一下。并尝试了很多方法。 已失效的老方法 // 定义一个检测 AdBlock 的函数 function chec…...

macOS 上部署 RAGFlow
在 macOS 上从源码部署 RAGFlow-0.14.1:详细指南 一、引言 RAGFlow 作为一款强大的工具,在人工智能领域应用广泛。本文将详细介绍如何在 macOS 系统上从源码部署 RAGFlow 0.14.1 版本,无论是开发人员进行项目实践,还是技术爱好者…...

如何在Kickstart自动化安装完成后ISO内拷贝文件到新系统或者执行命令
如何在Kickstart自动化安装完成后ISO内拷贝文件到新系统或者执行命令 需求 在自动化安装操作系统完成后,需要对操作系统进行配置需要拷贝一些文件到新的操作系统中需要运行一些脚本 问题分析 Linux安装操作系统时,实际上是将ISO镜像文件中的操作系统…...

在服务器部署JVM后,如何评估JVM的工作能力,比如吞吐量
在服务器部署JVM后,评估其工作能力(如吞吐量)可以通过以下步骤进行: 1. 选择合适的基准测试工具 JMH (Java Microbenchmark Harness):适合微基准测试,测量特定代码片段的性能。Apache JMeter:…...