SpringBoot项目中使用poi-tl打成jar包后常见问题及解决

目录

前言

一、场景描述

1、打成jar包运行后模板找不到

2、文件只能下载一次

二、正确示范

1、Controller下载方法定义

2、文档生成

总结


前言

        在前面的博客中,介绍了如何在Java中根据模板动态写入数据到word模板中,原文地址:Java使用poi-tl1.9.1生成Word文档的几个小技巧。这里给出的案例是直接生成到本地目录中,在实际项目开发过程中,一般会采用web请求的方式,动态的将数据设置到Word中,同时将文件下载到本地。

        在基于SpringBoot的开发环境中,我们经常会将实际部署的包打包成jar包的形式进行部署。在以上的场景开发中,不知道您是否会遇到以下的问题。比如会遇到模板找不到报错,还有请求智能点击一次的问题产生。

        本文将针对以上两种情况,分析并解决在SpringBoot环境中,将Poi-tl应用打包成jar后的实际运行问题进行解决。如果在平常开发过程中也遇到这种问题,可以试试博文的方案,看是否可以解决您的问题。

一、场景描述

1、打成jar包运行后模板找不到

java.io.FileNotFoundException: class path resource [report/temp.docx] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/ROOT/xxx.jar!/BOOT-INF/lib/xxx-2.1.jar!/report/temp.docx at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:217) at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:154) ...

        通常来说,如果遇到上述的这种异常错误信息,一般都是因为获取docx模板的代码不对导致的,知道了问题的关键就可以针对性进行解决。

解决方案

        在springboot项目中,对于打成jar包后的项目,如果想正常访问resource目录下的文件,建议采用以下的代码进行修复。

InputStream is = this.getClass().getClassLoader().getResourceAsStream("report/temp.docx");

        在word模板写入的时候,可以采用以下的代码:

template = XWPFTemplate.compile(is).render(map);

XWPFTemplate模板在写出的时候,可以支持直接对一个输入流进行内容写入。

2、文件只能下载一次

        在使用poi-tl生成word的时候,可能会遇到第一次请求下载文件是正常的,但是在第二次需要下载时浏览器则失去响应,就像假死一样。可能前端的小伙伴还会把锅刷到浏览器头上。这里需要注意的是,在进行IO读写的时候,千万要记得关流。

        如果你也遇到这种失去响应的情况,通常是因为忘记关流了。这里分享一段错误的代码示范:

private String createReport() throws Exception{String reportDir = RuoYiConfig.getProfile() + "/report";InputStream is = null;XWPFTemplate template = null;FileOutputStream out = null;try {is = this.getClass().getClassLoader().getResourceAsStream("report/temp.docx");Map<String, Object> map = new HashMap<String,Object>();map.put("score", "28");map.put("title", "测试任务");map.put("type", "上半年综合测评");map.put("status", "已完成");map.put("time", "2023-07-18");map.put("locpicture", new PictureRenderData(400, 300, "D:/image.jpg"));map.put("urlImg", Pictures.ofUrl("https://p1.itc.cn/images01/20230418/5d13ab4a86c04a8dac668bf4129e1f0c.png", PictureType.PNG).size(400, 300).create());ChartMultiSeriesRenderData sbqk = Charts.ofMultiSeries("十六市区县情况", new String[] { "济南","青岛","烟台","威海"}).addSeries("上报情况", new Double[] { 15.0,20.6,42.6,90.1}).addSeries("查出情况", new Double[] { 12.0,15.3,28.6,80.1}).create();map.put("sbqk", sbqk);ChartMultiSeriesRenderData sjzlpm = Charts.ofMultiSeries("医院综合排名", new String[] { "山东大学齐鲁医院","山东省泰山医院","山东省第二人民医院","山东省第三医院"}).addSeries("数据质量排名", new Double[] { 70.5,40.6,22.7,85.4}).addSeries("价格质量排名", new Double[] { 80.5,75.6,72.7,85.4}).create();map.put("sjzlpm", sjzlpm);ChartMultiSeriesRenderData qst = Charts.ofMultiSeries("任务趋势", new String[] { "06-10","06-11","06-12","06-13","06-14","06-15"}).addSeries("微信端", new Double[] { 70.5,40.6,22.7,85.4,700.0,40.8}).addSeries("PC端", new Double[] { 80.5,50.6,62.7,45.4,200.0,140.8}).addSeries("小程序端", new Double[] { 120.5,520.6,362.7,405.4,300.0,340.8}).create();map.put("qst", qst);//柱状图、折线图共存List<SeriesRenderData> seriesRenderData = new ArrayList<SeriesRenderData>(3);SeriesRenderData series1 = new SeriesRenderData("GDP", new Double[] {70.5,40.6,22.7,85.4,700.0,40.8});series1.setComboType(SeriesRenderData.ComboType.BAR);seriesRenderData.add(series1);SeriesRenderData series2 = new SeriesRenderData("人口", new Double[] {80.5,50.6,62.7,45.4,200.0,140.8});series2.setComboType(SeriesRenderData.ComboType.BAR);seriesRenderData.add(series2);SeriesRenderData series3 = new SeriesRenderData("指数", new Double[] {0.6,0.6,0.7,0.4,0.7,0.8});series3.setComboType(SeriesRenderData.ComboType.LINE);seriesRenderData.add(series3);ChartMultiSeriesRenderData hntb = Charts.ofMultiSeries("某省社会排名", new String[] { "城市1","城市2","城市3","城市4","城市5","城市6"}).create();hntb.setSeriesDatas(seriesRenderData);map.put("hntb", hntb);template = XWPFTemplate.compile(is).render(map);File reportDirFile = new File(reportDir);if(!reportDirFile.exists()) {reportDirFile.mkdir();}out = new FileOutputStream(new File(reportDir + "/生成报告结果.docx"));template.write(out);out.flush();} catch (Exception e) {}finally {if(null != out)out.close();if(null != template) template.close();//if(null != is)is.close();}return reportDir + "/生成报告结果.docx";}

        这里将is的流关闭一行注释掉,模拟流未关闭的情况。此时在浏览器中进行文件下载,大概率会得到一个无法下载的错误。

二、正确示范

        为了方式大家在复制的过程中遇到错误的示范问题,这里给出一段参考代码,其中已经将核心敏感的数据进行处理掉,但具体的业务逻辑不变,供参考,模板信息如下。

1、Controller下载方法定义

        在基于Ruoyi的SpringBoot项目开发中,需要定义一个用于接收前台请求的下载对象及处理方法,参考代码如下:

package com.ruoyi.project.demo.controller;import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import javax.servlet.http.HttpServletResponse;import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.data.ChartMultiSeriesRenderData;
import com.deepoove.poi.data.Charts;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.data.SeriesRenderData;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.RuoYiConfig;
import com.ruoyi.framework.web.controller.BaseController;@Controller
@RequestMapping("/demo/wordownload")
public class WordDownLoadController extends BaseController{@GetMapping("/test")public void test(HttpServletResponse response) throws Exception {String realFileName = "生成报告结果.docx";String filePath = this.createReport();response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);FileUtils.setAttachmentResponseHeader(response, realFileName);FileUtils.writeBytes(filePath, response.getOutputStream());}}

2、文档生成

private String createReport() throws Exception{String reportDir = RuoYiConfig.getProfile() + "/report";InputStream is = null;XWPFTemplate template = null;FileOutputStream out = null;try {is = this.getClass().getClassLoader().getResourceAsStream("report/temp.docx");Map<String, Object> map = new HashMap<String,Object>();map.put("score", "28");map.put("title", "测试任务");map.put("type", "上半年综合测评");map.put("status", "已完成");map.put("time", "2023-07-18");map.put("locpicture", new PictureRenderData(400, 300, "D:/image.jpg"));map.put("urlImg", Pictures.ofUrl("https://p1.itc.cn/images01/20230418/5d13ab4a86c04a8dac668bf4129e1f0c.png", PictureType.PNG).size(400, 300).create());ChartMultiSeriesRenderData sbqk = Charts.ofMultiSeries("十六市区县情况", new String[] { "济南","青岛","烟台","威海"}).addSeries("上报情况", new Double[] { 15.0,20.6,42.6,90.1}).addSeries("查出情况", new Double[] { 12.0,15.3,28.6,80.1}).create();map.put("sbqk", sbqk);ChartMultiSeriesRenderData sjzlpm = Charts.ofMultiSeries("医院综合排名", new String[] { "山东大学齐鲁医院","山东省泰山医院","山东省第二人民医院","山东省第三医院"}).addSeries("数据质量排名", new Double[] { 70.5,40.6,22.7,85.4}).addSeries("价格质量排名", new Double[] { 80.5,75.6,72.7,85.4}).create();map.put("sjzlpm", sjzlpm);ChartMultiSeriesRenderData qst = Charts.ofMultiSeries("任务趋势", new String[] { "06-10","06-11","06-12","06-13","06-14","06-15"}).addSeries("微信端", new Double[] { 70.5,40.6,22.7,85.4,700.0,40.8}).addSeries("PC端", new Double[] { 80.5,50.6,62.7,45.4,200.0,140.8}).addSeries("小程序端", new Double[] { 120.5,520.6,362.7,405.4,300.0,340.8}).create();map.put("qst", qst);//柱状图、折线图共存List<SeriesRenderData> seriesRenderData = new ArrayList<SeriesRenderData>(3);SeriesRenderData series1 = new SeriesRenderData("GDP", new Double[] {70.5,40.6,22.7,85.4,700.0,40.8});series1.setComboType(SeriesRenderData.ComboType.BAR);seriesRenderData.add(series1);SeriesRenderData series2 = new SeriesRenderData("人口", new Double[] {80.5,50.6,62.7,45.4,200.0,140.8});series2.setComboType(SeriesRenderData.ComboType.BAR);seriesRenderData.add(series2);SeriesRenderData series3 = new SeriesRenderData("指数", new Double[] {0.6,0.6,0.7,0.4,0.7,0.8});series3.setComboType(SeriesRenderData.ComboType.LINE);seriesRenderData.add(series3);ChartMultiSeriesRenderData hntb = Charts.ofMultiSeries("某省社会排名", new String[] { "城市1","城市2","城市3","城市4","城市5","城市6"}).create();hntb.setSeriesDatas(seriesRenderData);map.put("hntb", hntb);template = XWPFTemplate.compile(is).render(map);File reportDirFile = new File(reportDir);if(!reportDirFile.exists()) {reportDirFile.mkdir();}out = new FileOutputStream(new File(reportDir + "/生成报告结果.docx"));template.write(out);out.flush();} catch (Exception e) {}finally {if(null != out)out.close();if(null != template) template.close();if(null != is)is.close();}return reportDir + "/生成报告结果.docx";}

总结

        以上就是本文的主要内容,文章主要讲解了在SpringBoot中使用Poi-tl进行模板生成时,容易遇到的两个异常场景,以及针对性的给出了相应的解决方案。以上代码经过实际代码运行测试,可以正常使用。行文仓促,如有不当,尽请批评指正。

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

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

相关文章

Pandas数据分析一览-短期内快速学会数据分析指南(文末送书)

前言 三年耕耘大厂数据分析师&#xff0c;有些工具是必须要掌握的&#xff0c;尤其是Python中的数据分析三剑客&#xff1a;Pandas&#xff0c;Numpy和Matplotlib。就以个人经验而已&#xff0c;Pandas是必须要掌握的&#xff0c;它提供了易于使用的数据结构和数据操作工具&am…

第69步 时间序列建模实战:ARIMA建模(R)

基于WIN10的64位系统演示 一、写在前面 这一期&#xff0c;我们使用R进行SARIMA模型的构建。 同样&#xff0c;这里使用这个数据&#xff1a; 《PLoS One》2015年一篇题目为《Comparison of Two Hybrid Models for Forecasting the Incidence of Hemorrhagic Fever with Re…

uniapp小程序 - 隐私协议保护指引接入教程

文章目录 前提&#xff1a;__usePrivacyCheck__: true步骤一、封装弹窗组件步骤二、单个页面引用一、被动监听二、主动查询 前言&#xff1a;官方发布公告&#xff0c;自2023年9月15日起&#xff0c;对于涉及处理用户个人信息的小程序开发者&#xff0c;仅当开发者主动向平台同…

idea VCS配置多个远程仓库

Idea VCS配置多个远程仓库 首先要有两个或多个不同远程仓库地址 idea 添加数据源 查看推送记录 添加数据源 ok之后填写账号密码 推送本地项目 选择不同远程地址 push 查看不同远程地址的 不同分支的 推送记录 不期而遇的温柔&#xff1a; 应用开源架构进行项目开发&#xff0…

【新版】系统架构设计师 - 软件架构设计<新版>

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 软件架构设计&#xff1c;新版&#xff1e;考点摘要概念架构的 4 1 视图架构描述语言ADL基于架构的软件开发方法ABSDABSD的开发模型ABSDMABSD&#xff08;ABSDM模型&#xff09;的开发过程 软件架…

k8s node环境部署(三)

1、添加node1、node2环境 前面配置master环境的截图最后一段 复制下来 分别在node主机执行 kubeadm join 192.168.37.132:6443 --token p5omh3.cqjqt8ymrwkdn2fc \ --discovery-token-ca-cert-hash sha256:608a1cbadd060cfdeac2fae84c19609061b750ab51bf9a19887ff7ea…

C# 辗转相除法求最大公约数

辗转相除法求最大公约数 public static void CalcGCD(int largeNumber, int smallNumber, out int GCD){GCD 1;int remain -1;while (remain ! 0){remain largeNumber % smallNumber;GCD smallNumber;largeNumber smallNumber;smallNumber remain;}}

从零基础到精通Flutter开发:一步步打造跨平台应用

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 导言 Flutter是一种流行…

Java开发之框架(spring、springmvc、springboot、mybatis)【面试篇 完结版】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、框架知识分布二、Spring1. spring-单例bean① 问题引入② 单例bean是线程安全的吗③ 问题总结④ 实战面试 2. spring-AOP① 问题引入② AOP记录操作日志③ …

基于SSM+Vue的中国咖啡文化宣传网站

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用vUE技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【JAVA-Day09】 Java注释详解:一般注释、文档注释与最佳实践

Java注释详解&#xff1a;一般注释、文档注释与最佳实践 Java注释详解&#xff1a;一般注释、文档注释与最佳实践摘要引言一、一般注释1.1 块注释1.2 单行注释1.3 尾端注释 二、文档注释三、注释的最佳实践四、总结参考资料 博主 默语带您 Go to New World. ✍ 个人主页—— 默…

TheRouter 框架原理

TheRouter 框架入口方法 通过InnerTheRouterContentProvider 注册在AndroidManifest.xml中&#xff0c;在应用启动时初始化 <application><providerandroid:name"com.therouter.InnerTheRouterContentProvider"android:authorities"${applicationId}.…

【AI】机器学习——线性模型(线性回归)

线性模型既能体现出重要的基本思想&#xff0c;又能构造出功能更加强大的非线性模型 参考&#xff1a;唐宇迪机器学习课程 文章目录 3.1 线性模型3.1.1 数据3.1.2 目标/应用 3.2 线性回归3.2.1 回归模型历史3.2.2 回归分析研究内容回归分析步骤 3.2.3 回归分析分类3.2.4 回归模…

Flutter的oktoast插件详解

文章目录 简介详细介绍安装和导入导入在MaterialApp外面套一层OKToast组件为什么是包住MaterialApp&#xff1f; 显示Toast消息&#xff1a; 高级使用Toast位置Toast持续时间自定义Toast样式高级用法 使用场景提示消息表单验证操作反馈网络请求状态调试信息小结 总结 简介 okt…

数据治理中的核心元素——元数据

一、什么是元数据&#xff1f; 元数据是关于数据的组织&#xff0c;数据域及其关系的信息&#xff0c;简单来说&#xff0c;元数据就是被用来描述数据的信息。元数据是一个涵盖大量信息的数据集合。元数据可以为数据说明其元素或者属性&#xff08;名称&#xff0c;大小&#x…

029:vue项目,勾选后今天不再弹窗提示

第029个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

多元函数的微分法

目录 复合函数微分法 隐函数微分法 复合函数求导与全微分 隐函数偏导数与全微分 复合函数微分法 复合函数微分法是一种求导方法&#xff0c;用于计算复合函数的导数。 假设有一个复合函数yf(u)&#xff0c;其中ug(x)&#xff0c;则复合函数微分法可以用于计算y对x的导数。根…

使用SpringCloud Eureka 搭建EurekaServer 集群- 实现负载均衡故障容错【上】

&#x1f600;前言 本篇博文是关于使用SpringCloud Eureka 搭建EurekaServer 集群- 实现负载均衡&故障容错&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可…

Kafka3.0.0版本——消费者(独立消费者消费某一个主题数据案例__订阅主题)

目录 一、独立消费者消费某一个主题数据案例1.1、案例需求1.2、案例代码1.3、测试 一、独立消费者消费某一个主题数据案例 1.1、案例需求 创建一个独立消费者&#xff0c;消费firstTopic主题中数据&#xff0c;所下图所示&#xff1a; 注意&#xff1a;在消费者 API 代码中必…

JavaScript的基本数据类型如何使用?

JavaScript中的数据类型分为两大类&#xff0c;分别是基本数据类型和复杂数据类型(或称为引用数据类型)&#xff0c;如图所示。 本节重点讲解基本数据类型。下面我们用代码演示基本数据类型的使用。 (1)数字型(Number)&#xff0c;包含整型值和浮点型值: var numl 21; …