Java使用POI-TL和JFreeChart動(dòng)態(tài)生成Word報(bào)告
前言
在開發(fā)過程中,我們經(jīng)常需要生成包含動(dòng)態(tài)數(shù)據(jù)和圖表的 Word 報(bào)告。本文將介紹如何結(jié)合 POI-TL 和 JFreeChart,實(shí)現(xiàn)動(dòng)態(tài)生成 Word 報(bào)告的功能,并分享一些實(shí)際開發(fā)中的踩坑經(jīng)驗(yàn)。
word生成方案:
- freemarker+ftl
- pot-tl模板替換
- poi硬編碼
一、需求背景
在之前的文章中,我們已經(jīng)介紹了如何使用模板替換、復(fù)雜表格和圖片插入等功能。此次的需求是生成一個(gè)包含統(tǒng)計(jì)圖的 Word 報(bào)告,統(tǒng)計(jì)圖需要根據(jù)動(dòng)態(tài)數(shù)據(jù)生成。面臨的主要問題包括:
- 選擇 Word 生成方案:如何在 Word 中動(dòng)態(tài)插入數(shù)據(jù)和圖表?
- 圖片插入方案:如何將生成的統(tǒng)計(jì)圖插入到 Word 中?
- 生成統(tǒng)計(jì)圖表方案:如何根據(jù)數(shù)據(jù)動(dòng)態(tài)生成統(tǒng)計(jì)圖?
二、方案分析
- POI 硬編碼
直接使用 Apache POI 硬編碼生成 Word 文檔,雖然可行,但代碼復(fù)雜且難以維護(hù),因此不推薦。 - FreeMarker + FTL
FreeMarker 可以實(shí)現(xiàn)文本替換和圖片插入,理論上符合需求。但 FTL 模板的維護(hù)較為繁瑣,尤其是在處理復(fù)雜表格和圖片時(shí)。 - POI-TL + JFreeChart
POI-TL 是一個(gè)基于 Apache POI 的模板引擎,支持文本替換、圖片插入等功能。結(jié)合 JFreeChart 生成統(tǒng)計(jì)圖,可以很好地滿足需求。
三、 POI-TL + JFreeChart 實(shí)現(xiàn)
關(guān)于JFreeChart請移步Java使用JFreeChart創(chuàng)建動(dòng)態(tài)圖表的代碼示例_java_腳本之家
3.1 Maven 依賴
首先,需要在項(xiàng)目中引入 POI-TL 和 JFreeChart 的依賴。注意 POI 和 POI-TL 的版本需要對應(yīng),否則可能會(huì)出現(xiàn) NoSuchMethod 等錯(cuò)誤。
<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模板設(shè)置
在 Word 模板中,使用占位符標(biāo)記需要替換的內(nèi)容。對于圖片,需要在占位符前加 @,例如:
- 替換文本:時(shí)間 -> ${date}
- 插入圖片:${@dailyOnlinePic}
3.3 實(shí)現(xiàn)代碼
以下是核心實(shí)現(xiàn)代碼:
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); // 構(gòu)建模板替換的數(shù)據(jù) 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<>(); // 設(shè)置日期和在線總數(shù) dataMap.put("date", LocalDate.now()); // 查詢設(shè)備數(shù)據(jù) dataMap.put("onlineTotal", 100); // 生成每日在線圖表 DefaultCategoryDataset dailyData = buildDailyDataset(stationByTime); String dailyPicFile = generateChart(dailyData, "監(jiān)測站總在線數(shù)", "小時(shí)", "數(shù)量", dailyOnlineTxtEnum); dataMap.put("dailyOnlinePic", Pictures.ofStream(new FileInputStream(dailyPicFile), PictureType.JPEG).size(600, 200).create()); return dataMap; } // 構(gòu)建每日在線圖表的數(shù)據(jù)集 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 { // 設(shè)置全局字體(支持中文) StandardChartTheme chartTheme = new StandardChartTheme("CN"); chartTheme.setExtraLargeFont(new Font("宋體", Font.PLAIN, 14)); // 標(biāo)題字體 chartTheme.setLargeFont(new Font("宋體", Font.PLAIN, 14)); // 圖例字體 chartTheme.setRegularFont(new Font("宋體", Font.PLAIN, 12)); // 軸標(biāo)簽字體 ChartFactory.setChartTheme(chartTheme); // 創(chuàng)建圖表 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); // 每個(gè)類別寬度為50,最小寬度為800 ChartUtils.saveChartAsPNG(new File(picFile), chart, 1200, 400); return picFile; } // 設(shè)置圖表樣式 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); // 設(shè)置第一條折線的粗細(xì) plot.getRenderer().setSeriesStroke(0, new BasicStroke(5.0f)); // 根據(jù)樣式設(shè)置折線顏色 plot.getRenderer().setSeriesPaint(0, Color.RED); }
效果
踩坑
- 插入圖片如何占位
與常規(guī)文本替換不同,圖片插入需要在占位符前加 @,例如${@dailyOnlinePic}
或{{@pic}}
- 統(tǒng)計(jì)圖中文亂碼
JFreeChart 默認(rèn)不支持中文,需要通過設(shè)置全局字體解決:
// 設(shè)置全局字體(支持中文) StandardChartTheme chartTheme = new StandardChartTheme("CN"); chartTheme.setExtraLargeFont(new Font("宋體", Font.PLAIN, 14)); // 標(biāo)題字體 chartTheme.setLargeFont(new Font("宋體", Font.PLAIN, 14)); // 圖例字體 chartTheme.setRegularFont(new Font("宋體", Font.PLAIN, 12)); // 軸標(biāo)簽字體 ChartFactory.setChartTheme(chartTheme);
- 統(tǒng)計(jì)圖橫坐標(biāo)…
如果生成的圖片寬度不夠,橫坐標(biāo)可能會(huì)顯示不全??梢酝ㄟ^增加圖片寬度解決,插入時(shí)等比例縮放:
ChartUtils.saveChartAsPNG(new File(picFile), chart, 1200, 400);
dataMap.put("dailyOnlinePic", Pictures.ofStream(new FileInputStream(dailyPicFile), PictureType.JPEG).size(600, 200).create());
或動(dòng)態(tài)計(jì)算需要生成的圖片寬度:
int numberOfCategories = dataset.getColumnCount(); int width = Math.max(800, numberOfCategories * 50); // 每個(gè)類別寬度為50,最小寬度為800
到此這篇關(guān)于Java使用POI-TL和JFreeChart動(dòng)態(tài)生成Word報(bào)告的文章就介紹到這了,更多相關(guān)Java POI-TL JFreeChart生成Word報(bào)告t內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Reactor3 Map與FlatMap的區(qū)別示例詳解
這篇文章主要為大家介紹了Reactor3 Map與FlatMap的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08springboot 排除redis的自動(dòng)配置操作
這篇文章主要介紹了springboot 排除redis的自動(dòng)配置操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07Java 中執(zhí)行動(dòng)態(tài)表達(dá)式語句前中后綴Ognl、SpEL、Groovy、Jexl3
這篇文章主要介紹了Java 中執(zhí)行動(dòng)態(tài)表達(dá)式語時(shí)的句前中后綴Ognl、SpEL、Groovy、Jexl3的相關(guān)資料,需要的朋友可以參考下面文章的詳細(xì)介紹2021-09-09Java中的FutureTask實(shí)現(xiàn)代碼實(shí)例
這篇文章主要介紹了Java中的FutureTask手寫代碼實(shí)例,FutureTask是Future的實(shí)現(xiàn),用來異步任務(wù)的獲取結(jié)果,可以啟動(dòng)和取消異步任務(wù),查詢異步任務(wù)是否計(jì)算結(jié)束以及獲取最終的異步任務(wù)的結(jié)果,需要的朋友可以參考下2023-12-12SpringBoot?@Scheduled?Cron表達(dá)式使用方式
這篇文章主要介紹了SpringBoot?@Scheduled?Cron表達(dá)式使用方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03SpringBoot集成Druid配置(yaml版本配置文件)詳解
這篇文章主要介紹了SpringBoot集成Druid配置(yaml版本配置文件),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12java http連接池的實(shí)現(xiàn)方式(帶有失敗重試等高級功能)
這篇文章主要介紹了java http連接池的實(shí)現(xiàn)方式(帶有失敗重試等高級功能),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04