• 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包运行后模板找不到

    1. 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
    2. at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:217)
    3. at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:154)
    4.    ...

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

    解决方案

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

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

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

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

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

    2、文件只能下载一次

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

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

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

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

    二、正确示范

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

    1、Controller下载方法定义

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

    1. package com.ruoyi.project.demo.controller;
    2. import java.io.File;
    3. import java.io.FileOutputStream;
    4. import java.io.InputStream;
    5. import java.io.UnsupportedEncodingException;
    6. import java.util.ArrayList;
    7. import java.util.HashMap;
    8. import java.util.List;
    9. import java.util.Map;
    10. import javax.servlet.http.HttpServletResponse;
    11. import org.springframework.http.MediaType;
    12. import org.springframework.stereotype.Controller;
    13. import org.springframework.util.ResourceUtils;
    14. import org.springframework.web.bind.annotation.GetMapping;
    15. import org.springframework.web.bind.annotation.RequestMapping;
    16. import com.deepoove.poi.XWPFTemplate;
    17. import com.deepoove.poi.data.ChartMultiSeriesRenderData;
    18. import com.deepoove.poi.data.Charts;
    19. import com.deepoove.poi.data.PictureRenderData;
    20. import com.deepoove.poi.data.PictureType;
    21. import com.deepoove.poi.data.Pictures;
    22. import com.deepoove.poi.data.SeriesRenderData;
    23. import com.ruoyi.common.utils.file.FileUtils;
    24. import com.ruoyi.framework.config.RuoYiConfig;
    25. import com.ruoyi.framework.web.controller.BaseController;
    26. @Controller
    27. @RequestMapping("/demo/wordownload")
    28. public class WordDownLoadController extends BaseController{
    29. @GetMapping("/test")
    30. public void test(HttpServletResponse response) throws Exception {
    31. String realFileName = "生成报告结果.docx";
    32. String filePath = this.createReport();
    33. response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
    34. FileUtils.setAttachmentResponseHeader(response, realFileName);
    35. FileUtils.writeBytes(filePath, response.getOutputStream());
    36. }
    37. }

    2、文档生成

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

    总结

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

  • 相关阅读:
    进程间并发通信-IO多路复用
    【RocketMq 系列】RocketMq 入门教程
    C++类和对象【2】—— 对象特性(构造函数、析构函数、拷贝构造函数、深浅拷贝、初始化列表、类对象作为成员类、静态成员变量及静态成员函数等。)
    JavaScript中 slice, substr 和 substring 的区别
    【c++刷题Day3】专题5数组标记&哈希T3
    一些知识汇总
    JAVA反射
    淘宝旺旺黑号API接口 信誉
    GMT 格式 转 标准日期格式
    Kotlin中的数组
  • 原文地址:https://blog.csdn.net/yelangkingwuzuhu/article/details/132885653