Java在excel中导出动态曲线图DEMO

1、环境

JDK8

POI 5.2.3

Springboot2.7

2、DEMO

pom

        <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency>

java

    @GetMapping("/export")public ResponseEntity<byte[]> exportExcel() throws IOException {// 创建工作簿和工作表XSSFWorkbook workbook = new XSSFWorkbook();XSSFSheet sheet = workbook.createSheet("Chart");// 自定义数据源List<String> xValues = Arrays.asList("A", "B", "C", "D", "E");List<Double> line1Values = Arrays.asList(10.0, 15.0, 20.0, 25.0, 30.0);List<Double> line2Values = Arrays.asList(5.0, 7.0, 9.0, 11.0, 13.0);XSSFSheet hiddenSheet = workbook.createSheet("Hidden Data");workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);Row hiddenRow = hiddenSheet.createRow(0);for (int i = 0; i < xValues.size(); i++) {Cell cell = hiddenRow.createCell(i);cell.setCellValue(xValues.get(i));}for (int i = 0; i < 10; i++) {createChart("title" + i, sheet,hiddenSheet,i,xValues,line1Values,i%2 == 0 ? line2Values: null);}// 将工作簿写入字节数组ByteArrayOutputStream outputStream = new ByteArrayOutputStream();workbook.write(outputStream);workbook.close();// 设置响应头HttpHeaders headersResponse = new HttpHeaders();headersResponse.setContentType(MediaType.APPLICATION_OCTET_STREAM);headersResponse.setContentDispositionFormData("attachment", "chart.xlsx");return new ResponseEntity<>(outputStream.toByteArray(), headersResponse, HttpStatus.OK);}private void createChart(String title, XSSFSheet sheet, XSSFSheet hiddenSheet, int index, List<String> xValues, List<Double> line1Values, List<Double> line2Values) {XSSFDrawing drawing = sheet.createDrawingPatriarch();XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 5, index * 21, 15, index * 21 + 20);XSSFChart chart = drawing.createChart(anchor);chart.setTitleText(title);chart.setTitleOverlay(false);XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);xAxis.setTitle(" ");XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.LEFT);yAxis.setTitle(" ");XDDFDataSource<String> xs = XDDFDataSourcesFactory.fromStringCellRange(hiddenSheet,new CellRangeAddress(0, 0, 0,  xValues.size() - 1));XDDFLineChartData lineChartData = (XDDFLineChartData) chart.createData(ChartTypes.LINE, xAxis, yAxis);XDDFNumericalDataSource<Double> line1 = XDDFDataSourcesFactory.fromArray(line1Values.toArray(new Double[0]));XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) lineChartData.addSeries(xs, line1);if (CollUtil.isNotEmpty(line2Values)) {XDDFNumericalDataSource<Double> line2 = XDDFDataSourcesFactory.fromArray(line2Values.toArray(new Double[0]));XDDFLineChartData.Series series2 = (XDDFLineChartData.Series) lineChartData.addSeries(xs, line2);}chart.plot(lineChartData);}

说明,封装了一个方法,createChart。

图表位置

XSSFClientAnchor anchor = drawing.createAnchor(dx1, dy1, dx2, dy2, col1, row1, col2, row2);

参数说明:

  • dx1dy1

    • 表示图表左上角相对于单元格左上角的偏移量(以 EMU 为单位)。
    • 1 EMU = 1/360000 厘米。
    • 如果你不需要精确控制偏移量,可以将这两个值设置为 0
  • dx2dy2

    • 表示图表右下角相对于单元格右下角的偏移量(以 EMU 为单位)。
    • 同样,如果你不需要精确控制偏移量,可以将这两个值设置为 0
  • col1row1

    • 表示图表左上角所在的单元格列号和行号(从 0 开始计数)。
    • 例如,col1 = 5 表示图表左上角位于第 6 列(F 列),row1 = 0 表示图表左上角位于第 1 行。
  • col2row2

    • 表示图表右下角所在的单元格列号和行号(从 0 开始计数)。
    • 例如,col2 = 15 表示图表右下角位于第 16 列(P 列),row2 = 20 表示图表右下角位于第 21 行。

hiddenSheet存在的目的是

1. Apache POI 的图表数据绑定机制

Apache POI 的图表功能是基于 Excel 的底层结构设计的。在 Excel 中,图表的数据源通常是从工作表中的单元格范围(CellRangeAddress)中读取的。也就是说,图表的 X 轴和 Y 轴数据必须绑定到某个单元格范围,即使这些单元格并不直接显示在工作表中。

核心原因:

  • Apache POI 的 XDDFDataSourcesFactory.fromStringCellRange() 方法要求传入一个有效的单元格范围。
  • 如果没有单元格范围,POI 不知道如何为图表提供数据源,因此会导致图表无法正确显示。

所以我采用了还是用了隐藏的方式。对于整个sheet的布局

3、效果

有个小bug

这个系列1这几个字去不掉,图表工具提示中的“系列名称”(如“系列1”)是由 Excel 自动生成的。虽然 Apache POI 不支持直接修改或自定义工具提示的内容

一下方法可以一试。

  // 隐藏图例(可选)XDDFChartLegend legend = chart.getOrAddLegend();legend.setPosition(LegendPosition.TOP); // 将图例移出可视区域

对于目前项目而言,影响不大。不改动了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80319.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android APP 爬虫操作

工具 夜神模拟器、charles、mitm 等 mitm的使用参考:Mitmproxy对Android进行抓包&#xff08;真机&#xff09;_mitmproxy 安卓-CSDN博客 charles的使用参考&#xff1a;【全网最详细】手把手教学Charles抓包工具详细自学教程&#xff0c;完整版安装教程&#xff0c;详细介绍…

Redis的LFU策略具体怎么工作?

Redis的LFU&#xff08;Least Frequently Used&#xff09;策略通过动态跟踪键的访问频率实现淘汰决策&#xff0c;其核心工作逻辑可分为以下四个部分&#xff1a; 数据结构设计‌ 字段拆分‌&#xff1a;每个Redis对象&#xff08;redisObject&#xff09;的lru字段&#xff…

Redis 及其在系统设计中的作用

什么是Redis Redis 是一个开源的内存数据结构存储系统&#xff0c;可用作数据库、缓存和消息代理。它因其快速的性能、灵活性和易用性而得到广泛应用。 Redis 数据存储类型 Redis 允许开发人员以各种数据结构&#xff08;例如字符串、位图、位域、哈希、列表、集合、有序集合…

MySQL:如何用关系型数据库征服NoSQL核心战场?

写在前面&#xff1a;当SQL遇见NoSQL的十年之变 2012年MongoDB掀起文档数据库革命时&#xff0c;开发者们不得不在灵活性与事务一致性之间做痛苦抉择。十年后的今天&#xff0c;MySQL 8.0的JSON功能已实现&#xff1a; ✅ 二进制存储效率超越传统BLOB 40% ✅ 多值索引使JSON查…

Dart Flutter数据类型详解 int double String bool list Map

目录 字符串的几种方式 bool值的判断 List的定义方式 Map的定义方式 Dart判断数据类型 (is 关键词来判断类型) Dart的数据类型详解 int double String bool list Map 常用数据类型: Numbers(数值): int double Strings(字符串) String Booleans(布尔…

win11中wsl在自定义位置安装ubuntu20.04 + ROS Noetic

wsl的安装 环境自定义位置安装指定ubuntu版本VsCodeROS备份与重载备份重新导入 常用命令参考文章 环境 搜索 启用或关闭 Windows 功能 勾选这2个功能&#xff0c;然后重启 自定义位置安装指定ubuntu版本 从网上找到你所需要的相关wsl ubuntu版本的安装包&#xff0c;一般直…

得物业务参数配置中心架构综述

一、背景 现状与痛点 在目前互联网飞速发展的今天&#xff0c;企业对用人的要求越来越高&#xff0c;尤其是后端的开发同学大部分精力都要投入在对复杂需求的处理&#xff0c;以及代码架构&#xff0c;稳定性的工作中&#xff0c;在对比下&#xff0c;简单且重复的CRUD就显得…

Nginx 二进制部署与 Docker 部署深度对比

一、核心概念解析 1. 二进制部署 通过包管理器&#xff08;如 apt/yum&#xff09;或源码编译安装 Nginx&#xff0c;直接运行在宿主机上。其特点包括&#xff1a; 直接性&#xff1a;与操作系统深度绑定&#xff0c;直接使用系统库和内核功能 。定制化&#xff1a;支持通过…

Rust 2025:内存安全革命与异步编程新纪元

Rust 2025 Edition通过区域内存管理、泛型关联类型和零成本异步框架三大革新&#xff0c;重新定义系统级编程语言的能力边界。本次升级不仅将内存安全验证效率提升80%&#xff0c;更通过异步执行器架构优化实现微秒级任务切换。本文从编译器原理、运行时机制、编程范式转型三个…

std::unorderd_map 简介

1. unorderd_map 简介 1. unorderd_map 简介 简介1.1. 实现原理1.2. 函数1.3. 问题集 1.3.1. emplace、emplace_hint、insert 的区别 1.4. 参考链接 简介 unordered_map 是 C 标准库中的一个容器&#xff0c;它定义在 <unordered_map> 头文件里。它借助哈希表来存储键…

在线测试来料公差

UI 上图 V1 上图 V2 V3 Code import tkinter as tk from tkinter import messagebox, scrolledtext import socket import threading from datetime import datetime import os import logging from PIL import Image, ImageTk import subprocess# 定义文件夹路径…

【优秀三方库研读】【C++基础知识】odygrd/quill -- 折叠表达式

compute_encoded_size_and_cache_string_lengths 方法中这段代码是一个C的折叠表达式&#xff08;fold expression&#xff09;的应用&#xff0c;用于计算多个参数编码后的总大小。下面我将详细解释这段代码的每个部分&#xff0c;并说明为什么这样写。 代码如下&#xff1a; …

数据库安装和升级和双主配置

备份和导入数据 ./mysqldump -u root -p123321 test > test.sql rsync -av test.sql root192.168.0.212:/usr/local/mysql/ ./mysql -uroot -p test < …/test.sql sudo tar -zxvf mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz -C /usr/local/ sudo ln -sfn /usr/loca…

【C语言】条件编译

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 条件编译 常用的预处理指令 核心应用场景 1.防止头文件重复包含 2.跨平台兼容性 3.调试模式与发布模式 4.功能开关 5.代码兼容性处理 结语 条件编译 一般情况下,源程序中所有…

如何在安卓平板上下载安装Google Chrome【轻松安装】

安卓平板可以通过系统内置的应用商店直接搜索并下载谷歌浏览器。用户打开平板上的“Play 商店”&#xff0c;在搜索框输入Google Chrome。出现结果后&#xff0c;点击第一个带有“Google LLC”字样的应用图标&#xff0c;然后点“安装”按钮。下载和安装时间和网速有关&#xf…

.NET代码保护混淆和软件许可系统——Eziriz .NET Reactor 7

.NET代码保护混淆和软件许可系统——Eziriz .NET Reactor 7 1、简介2、功能特点3、知识产权保护功能4、强大的许可系统5、软件开发工具包6、部署方式7、下载 1、简介 .NET Reactor是用于为.NET Framework编写的软件的功能强大的代码保护和软件许可系统&#xff0c;并且支持生成…

利用 SSE 实现文字吐字效果:技术与实践

利用 SSE 实现文字吐字效果:技术与实践 引言 在现代 Web 应用开发中,实时交互功能愈发重要。例如,在线聊天、实时数据监控、游戏中的实时更新等场景,都需要服务器能够及时将数据推送给客户端。传统的请求 - 响应模式在处理实时性要求较高的场景时显得力不从心,而 Server…

一个简单易用的密码生成器

基于浏览器的确定性密码生成工具&#xff0c;通过用户输入的网站名称和盐值生成符合安全要求的密码。特点&#xff1a; • 相同输入始终生成相同密码 • 密码自动包含大小写字母、数字和特殊符号 • 以字母开头&#xff0c;固定8位长度 • 完全在客户端运行&#xff0c;保护…

水上与水下遥控技术要点对比

1. 水上无人机遥控器技术要点 (1) 控制方式 多通道控制&#xff1a;通常使用2.4GHz或5.8GHz无线电信号&#xff0c;支持多通道&#xff08;如4通道以上&#xff09;分别控制飞行器的姿态&#xff08;俯仰、横滚、偏航&#xff09;和油门。 高级飞行模式&#xff1a;如定高模…

Android_SDK链接 雷神模拟器(端口问题) --- app笔记

调试环境&#xff1a;JDK&#xff08;java&#xff09; SDK&#xff08;android&#xff09; Node.js 雷神模拟器&#xff08;或 真机&#xff09; Appium&#xff08;Appium Server【内外件&#xff08;dos内件、界面化工具&#xff09;】、Appium Inspector&#xff09; p…