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

使用 POI-TL 和 JFreeChart 动态生成 Word 报告

文章目录

  • 前言
  • 一、需求背景
  • 二、方案分析
  • 三、 POI-TL + JFreeChart 实现
    • 3.1 Maven 依赖
    • 3.3 word模板设置
    • 3.2 实现代码
  • 踩坑


前言

在开发过程中,我们经常需要生成包含动态数据和图表的 Word 报告。本文将介绍如何结合 POI-TL 和 JFreeChart,实现动态生成 Word 报告的功能,并分享一些实际开发中的踩坑经验。
word生成方案:

  1. freemarker+ftl
  2. pot-tl模板替换
  3. poi硬编码

一、需求背景

在之前的文章中,我们已经介绍了如何使用模板替换、复杂表格和图片插入等功能。此次的需求是生成一个包含统计图的 Word 报告,统计图需要根据动态数据生成。面临的主要问题包括:

  1. 选择 Word 生成方案:如何在 Word 中动态插入数据和图表?
  2. 图片插入方案:如何将生成的统计图插入到 Word 中?
  3. 生成统计图表方案:如何根据数据动态生成统计图?

二、方案分析

  1. POI 硬编码
    直接使用 Apache POI 硬编码生成 Word 文档,虽然可行,但代码复杂且难以维护,因此不推荐。

  2. FreeMarker + FTL
    FreeMarker 可以实现文本替换和图片插入,理论上符合需求。但 FTL 模板的维护较为繁琐,尤其是在处理复杂表格和图片时。

  3. 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);}

效果
在这里插入图片描述


踩坑

  1. 插入图片如何占位
    与常规文本替换不同,图片插入需要在占位符前加 @,例如 ${@dailyOnlinePic}{{@pic}}
  2. 统计图中文乱码
    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
  1. 模板条件判断
    POI-TL支持复杂的条件判断,例如结合逻辑运算符(&&、||)和比较运算符(==、!=、>、< 等)。
    例如:
{{?isStudent}}{{?age > 18}}您是一名成年学生。{{/age > 18}}
{{/isStudent}}

此次实验boolean好用,字符串==匹配失败。


在这里插入图片描述

相关文章:

使用 POI-TL 和 JFreeChart 动态生成 Word 报告

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

xxl-job的分片广播

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

MobaXterm破解会话上限限制

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

vscode设置保存时自动缩进和格式化

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

一键查看电脑各硬件详细信息 轻松查看电脑硬件参数

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

【C++11】lambda和包装器

1.新的类功能 1.1默认的移动构造和移动赋值 原来C类中&#xff0c;有6个默认成员函数&#xff1a;构造函数/析构函数/拷⻉构造函数/拷⻉赋值重载/取地址重 载/const 取地址重载&#xff0c;最后重要的是前4个&#xff0c;后两个⽤处不⼤&#xff0c;默认成员函数就是我们不写…...

react redux用法学习

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

前端HTML标签 meta中常见的一些属性

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

127,【3】 buuctf [NPUCTF2020]ReadlezPHP

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

继承(python)

一、基础知识 &#xff08;一&#xff09;定义&#xff1a;子类能继承父类所有的公有属性和公有方法&#xff08;先使用子类的方法、属性&#xff09; &#xff08;二&#xff09;格式&#xff1a; class 子类名&#xff08;父类名&#xff09;&#xff1a; #父类 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示例地址 官网有的时候示例显示不出来&#xff0c;属于正常现象&#xff0c;多进几次就行 开始使用前&#xff0c;记得先…...

小红书自动化:如何利用Make批量生成爆款笔记

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

学习率调整策略 | PyTorch 深度学习实战

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

DeepSeekMoE 论文解读:混合专家架构的效能革新者

论文链接&#xff1a;DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models 目录 一、引言二、背景知识&#xff08;一&#xff09;MoE架构概述&#xff08;二&#xff09;现有MoE架构的问题 三、DeepSeekMoE架构详解&#xff08;一&a…...

以下是基于巨控GRM241Q-4I4D4QHE模块的液位远程控制系统技术方案:

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

【JVM详解五】JVM性能调优

示例&#xff1a; 配置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日学习总结

题目一&#xff1a; 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&#xff08;异步 JavaScript 和 XML&#xff09; 什么是 Ajax&#xff1f; Ajax 是一种用于在不刷新页面的情况下与服务器进行数据交互的技术。它通过 XMLHttpRequest 对象实现。 优点 - 支持同步和异步请求。 - 能…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?

Otsu 是一种自动阈值化方法&#xff0c;用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理&#xff0c;能够自动确定一个阈值&#xff0c;将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...

基于Docker Compose部署Java微服务项目

一. 创建根项目 根项目&#xff08;父项目&#xff09;主要用于依赖管理 一些需要注意的点&#xff1a; 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件&#xff0c;否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

Caliper 配置文件解析:config.yaml

Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...