Java使用POI-TL和JFreeChart動態(tài)生成Word報告
前言
在開發(fā)過程中,我們經(jīng)常需要生成包含動態(tài)數(shù)據(jù)和圖表的 Word 報告。本文將介紹如何結(jié)合 POI-TL 和 JFreeChart,實現(xiàn)動態(tài)生成 Word 報告的功能,并分享一些實際開發(fā)中的踩坑經(jīng)驗。
word生成方案:
- freemarker+ftl
- pot-tl模板替換
- poi硬編碼
一、需求背景
在之前的文章中,我們已經(jīng)介紹了如何使用模板替換、復(fù)雜表格和圖片插入等功能。此次的需求是生成一個包含統(tǒng)計圖的 Word 報告,統(tǒng)計圖需要根據(jù)動態(tài)數(shù)據(jù)生成。面臨的主要問題包括:
- 選擇 Word 生成方案:如何在 Word 中動態(tài)插入數(shù)據(jù)和圖表?
- 圖片插入方案:如何將生成的統(tǒng)計圖插入到 Word 中?
- 生成統(tǒng)計圖表方案:如何根據(jù)數(shù)據(jù)動態(tài)生成統(tǒng)計圖?
二、方案分析
- POI 硬編碼
直接使用 Apache POI 硬編碼生成 Word 文檔,雖然可行,但代碼復(fù)雜且難以維護,因此不推薦。 - FreeMarker + FTL
FreeMarker 可以實現(xiàn)文本替換和圖片插入,理論上符合需求。但 FTL 模板的維護較為繁瑣,尤其是在處理復(fù)雜表格和圖片時。 - POI-TL + JFreeChart
POI-TL 是一個基于 Apache POI 的模板引擎,支持文本替換、圖片插入等功能。結(jié)合 JFreeChart 生成統(tǒng)計圖,可以很好地滿足需求。
三、 POI-TL + JFreeChart 實現(xiàn)
關(guān)于JFreeChart請移步Java使用JFreeChart創(chuàng)建動態(tài)圖表的代碼示例_java_腳本之家
3.1 Maven 依賴
首先,需要在項目中引入 POI-TL 和 JFreeChart 的依賴。注意 POI 和 POI-TL 的版本需要對應(yīng),否則可能會出現(xiàn) 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模板設(shè)置
在 Word 模板中,使用占位符標(biāo)記需要替換的內(nèi)容。對于圖片,需要在占位符前加 @,例如:
- 替換文本:時間 -> ${date}
- 插入圖片:${@dailyOnlinePic}
3.3 實現(xiàn)代碼
以下是核心實現(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ù)量", 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); // 每個類別寬度為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è)置第一條折線的粗細 plot.getRenderer().setSeriesStroke(0, new BasicStroke(5.0f)); // 根據(jù)樣式設(shè)置折線顏色 plot.getRenderer().setSeriesPaint(0, Color.RED); }
效果
踩坑
- 插入圖片如何占位
與常規(guī)文本替換不同,圖片插入需要在占位符前加 @,例如${@dailyOnlinePic}
或{{@pic}}
- 統(tǒng)計圖中文亂碼
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)計圖橫坐標(biāo)…
如果生成的圖片寬度不夠,橫坐標(biāo)可能會顯示不全??梢酝ㄟ^增加圖片寬度解決,插入時等比例縮放:
ChartUtils.saveChartAsPNG(new File(picFile), chart, 1200, 400);
dataMap.put("dailyOnlinePic", Pictures.ofStream(new FileInputStream(dailyPicFile), PictureType.JPEG).size(600, 200).create());
或動態(tài)計算需要生成的圖片寬度:
int numberOfCategories = dataset.getColumnCount(); int width = Math.max(800, numberOfCategories * 50); // 每個類別寬度為50,最小寬度為800
到此這篇關(guān)于Java使用POI-TL和JFreeChart動態(tài)生成Word報告的文章就介紹到這了,更多相關(guān)Java POI-TL JFreeChart生成Word報告t內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot讀取外部配置文件,項目部署時配置讀取不到問題及解決
這篇文章主要介紹了Springboot讀取外部配置文件,項目部署時配置讀取不到問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06SpringBoot中使用?ThreadLocal?進行多線程上下文管理及注意事項小結(jié)
本文詳細介紹了ThreadLocal的原理、使用場景和示例代碼,并在SpringBoot中使用ThreadLocal保存請求中攜帶的用戶信息,ThreadLocal通過為每個線程維護獨立的變量副本,解決了線程安全問題,感興趣的朋友一起看看吧2025-02-02Spring?this調(diào)用當(dāng)前類方法無法攔截的示例代碼
這篇文章主要介紹了Spring?this調(diào)用當(dāng)前類方法無法攔截,通過debug 查看這個proxyService1 和this的區(qū)別,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-03-03Java框架Quartz中API、Jobs和Trigger詳解
這篇文章主要介紹了Java框架Quartz中API、Jobs和Trigger詳解,JobDetail?對象是在將?job?加入?scheduler?時,由客戶端程序(你的程序)創(chuàng)建的,它包含?job?的各種屬性設(shè)置,以及用于存儲?job?實例狀態(tài)信息的?JobDataMap,需要的朋友可以參考下2023-11-11Spring框架事務(wù)屬性中事務(wù)隔離級別與傳播行為全面講解
這篇文章主要介紹了Spring框架聲明式事務(wù)的事務(wù)隔離級別和事務(wù)傳播行為,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2022-11-11Java根據(jù)模板實現(xiàn)excel導(dǎo)出標(biāo)準(zhǔn)化
這篇文章主要為大家詳細介紹了Java如何根據(jù)模板實現(xiàn)excel導(dǎo)出標(biāo)準(zhǔn)化,文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考下2024-03-03java實現(xiàn)單鏈表中是否有環(huán)的方法詳解
本篇文章介紹了,用java實現(xiàn)單鏈表中是否有環(huán)的方法詳解。需要的朋友參考下2013-05-05