欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

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

来源: javaer 分享于  点击 13205 次 点评:65

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


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

    前言

    在开发过程中,我们经常需要生成包含动态数据和图表的 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请移步Java使用JFreeChart创建动态图表的代码示例_java_3672js教程

    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.2 word模板设置

    在 Word 模板中,使用占位符标记需要替换的内容。对于图片,需要在占位符前加 @,例如:

    • 替换文本:时间 -> ${date}
    • 插入图片:${@dailyOnlinePic}

    3.3 实现代码

    以下是核心实现代码:

        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,最小宽度为800
            ChartUtils.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);
    
    • 统计图横坐标…
      如果生成的图片宽度不够,横坐标可能会显示不全。可以通过增加图片宽度解决,插入时等比例缩放:
    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
    

    到此这篇关于Java使用POI-TL和JFreeChart动态生成Word报告的文章就介绍到这了,更多相关Java POI-TL JFreeChart生成Word报告t内容请搜索3672js教程以前的文章或继续浏览下面的相关文章希望大家以后多多支持3672js教程!

    您可能感兴趣的文章:
    • Java使用POI-TL实现生成有个性的简历
    • Java使用poi-tl1.9.1生成Word文档的技巧分享
    • SpringBoot调用Poi-tl实现渲染数据并生成Word文档
    • SpringBoot如何基于POI-tl和word模板导出庞大的Word文件
    相关栏目:

    用户点评