Java使用poi-tl1.9.1生成Word文檔的技巧分享
前言
也許在您的工作當中會碰到如下的一些場景,比如您需要組織一個活動,主辦方需要對每個報名參加的單位進行報名通知書的生成。也許您會說,參加活動的不多,可以采取人工的方式進行信息填寫,然后再發(fā)送給報名人員就好。如果僅是幾個人或者幾十個人還好,如果是幾百人,您還會選擇這種方式么?
針對這種批量動態(tài)個性化的word生成需求,有沒有什么技術可以進行輔助生成呢?一定是有的,在Java的世界中,實現這種需求有好幾種實現方案,本文推薦一種簡單快捷的實現方式,基于開源的poi-tl的實現機制。本文將簡單介紹poi-tl的相關知識,通過一個實際的案例實踐,充分介紹如何利用poi-tl進行目標文檔的生成,同時分享幾個不同的office版本如何進行圖表生成的解決方案。如果剛好您也在進行相關技術選型,本文可以作為使用參考。
一、poi-tl簡介
1、什么是poi-tl
poi-tl(poi template language)是Word模板引擎,使用Word模板和數據創(chuàng)建很棒的Word文檔。poi-tl是一款采用Apache License 2.0開源協(xié)議的開源產品,poi-tl github地址。

2、常見的word生成對比
| 方案 | 移植性 | 功能性 | 易用性 |
|---|---|---|---|
Poi-tl | Java跨平臺 | Word模板引擎,基于Apache POI,提供更友好的API | 低代碼,準備文檔模板和數據即可 |
Apache POI | Java跨平臺 | Apache項目,封裝了常見的文檔操作,也可以操作底層XML結構 | 文檔不全,這里有一個教程:Apache POI Word快速入門 |
Freemarker | XML跨平臺 | 僅支持文本,很大的局限性 | 不推薦,XML結構的代碼幾乎無法維護 |
OpenOffice | 部署OpenOffice,移植性較差 | - | 需要了解OpenOffice的API |
HTML瀏覽器導出 | 依賴瀏覽器的實現,移植性較差 | HTML不能很好的兼容Word的格式,樣式糟糕 | - |
Jacob、winlib | Windows平臺 | - | 復雜,完全不推薦使用 |
3、poi-tl功能點
| d模板引擎功能 | 描述 |
|---|---|
文本 | 將標簽渲染為文本 |
圖片 | 將標簽渲染為圖片 |
表格 | 將標簽渲染為表格 |
列表 | 將標簽渲染為列表 |
圖表 | 條形圖(3D條形圖)、柱形圖(3D柱形圖)、面積圖(3D面積圖)、折線圖(3D折線圖)、雷達圖、餅圖(3D餅圖)、散點圖等圖表渲染 |
If Condition判斷 | 根據條件隱藏或者顯示某些文檔內容(包括文本、段落、圖片、表格、列表、圖表等) |
Foreach Loop循環(huán) | 根據集合循環(huán)某些文檔內容(包括文本、段落、圖片、表格、列表、圖表等) |
Loop表格行 | 循環(huán)復制渲染表格的某一行 |
Loop表格列 | 循環(huán)復制渲染表格的某一列 |
Loop有序列表 | 支持有序列表的循環(huán),同時支持多級列表 |
Highlight代碼高亮 | word中代碼塊高亮展示,支持26種語言和上百種著色樣式 |
Markdown | 將Markdown渲染為word文檔 |
Word批注 | 完整的批注功能,創(chuàng)建批注、修改批注等 |
Word附件 | Word中插入附件 |
SDT內容控件 | 內容控件內標簽支持 |
Textbox文本框 | 文本框內標簽支持 |
圖片替換 | 將原有圖片替換成另一張圖片 |
書簽、錨點、超鏈接 | 支持設置書簽,文檔內錨點和超鏈接功能 |
Expression Language | 完全支持SpringEL表達式,可以擴展更多的表達式:OGNL, MVEL…? |
樣式 | 模板即樣式,同時代碼也可以設置樣式 |
模板嵌套 | 模板包含子模板,子模板再包含子模板 |
合并 | Word合并Merge,也可以在指定位置進行合并 |
用戶自定義函數(插件) | 插件化設計,在文檔任何位置執(zhí)行函數 |
二、poi-tl文檔生成
通常來說,我們會先制作好一個標準的參考模板,讓后將需要替換的數據替換到目標參數中,完成相應參數的替換,所以剛開始先來準備一分word模板。
1、模板準備
在電腦的任意盤符,這里以D盤為例,創(chuàng)建一個文件輸入.docx的word文檔,打開文檔編輯

在這個模板中定義了文本標簽以及圖片的定義。這里注意的是,{{}}這對標準參數符必須是英文狀態(tài)下輸入,之前有朋友就是沒注意中英文,導致程序沒有按照預期出來。同時注意圖片的引用符在參數名稱前面加上英文@符號。

在模板中加入一些圖表信息,豐富素材,這里需要設置。我個人電腦上的圖表參數設置如下(尤其注意不同的office版本,對應的編輯處理方法不一致):

2、目標參數填充
首先在工程中進行pom.xml引入
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.yelang</groupId> <artifactId>poi-demo2</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.9.1</version> </dependency> </dependencies> </project>
參數填充:這里為了演示方便,直接構造演示數據,真實項目中可以使從數據庫或者其它接口獲取相應的數據來進行參數的設置。
package com.yelang.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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;
public class TestMain2 {
public static void main(String[] args) throws IOException {
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("十六市區(qū)縣情況", 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("醫(yī)院綜合排名", new String[] { "山東大學齊魯醫(yī)院","山東省泰山醫(yī)院","山東省第二人民醫(yī)院","山東省第三醫(yī)院"})
.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);
File file = new File("D:/文件輸入.docx");
XWPFTemplate template = XWPFTemplate.compile(file).render(map);
FileOutputStream out = new FileOutputStream(new File("D:\\文件輸出.docx"));
template.write(out);
out.flush();
out.close();
template.close();
System.out.println("完成");
}
}3、生成效果
將以上代碼運行后,可以在D盤目錄中看到以下的輸出結果。


三、可能會遇到的問題
1、混合圖表生成報錯
如果在代碼運行過程中遇到以下異常:
Exception in thread "main" com.deepoove.poi.exception.RenderException: Combo chart must set comboType field of series! at com.deepoove.poi.policy.reference.MultiSeriesChartTemplateRenderPolicy.validate(MultiSeriesChartTemplateRenderPolicy.java:122) at com.deepoove.poi.policy.reference.MultiSeriesChartTemplateRenderPolicy.doRender(MultiSeriesChartTemplateRenderPolicy.java:56) at com.deepoove.poi.policy.reference.MultiSeriesChartTemplateRenderPolicy.doRender(MultiSeriesChartTemplateRenderPolicy.java:48) at com.deepoove.poi.policy.reference.AbstractTemplateRenderPolicy.render(AbstractTemplateRenderPolicy.java:38) at com.deepoove.poi.render.processor.ElementProcessor.visit(ElementProcessor.java:70) at com.deepoove.poi.render.processor.ElementProcessor.visit(ElementProcessor.java:56) at com.deepoove.poi.template.ChartTemplate.accept(ChartTemplate.java:117) at com.deepoove.poi.render.processor.DocumentProcessor.visit(DocumentProcessor.java:104) at com.deepoove.poi.template.ChartTemplate.accept(ChartTemplate.java:117) at com.deepoove.poi.render.processor.DocumentProcessor.lambda$process$0(DocumentProcessor.java:58) at java.util.ArrayList.forEach(Unknown Source) at com.deepoove.poi.render.processor.DocumentProcessor.process(DocumentProcessor.java:58) at com.deepoove.poi.render.DefaultRender.renderTemplate(DefaultRender.java:82) at com.deepoove.poi.render.DefaultRender.render(DefaultRender.java:64) at com.deepoove.poi.XWPFTemplate.render(XWPFTemplate.java:127) at com.yelang.test.TestMain2.main(TestMain2.java:87)
Combo chart must set comboType field of series!這個問題大概率是因為圖表生成沒有指定圖表類型。只需要的代碼中指定圖表類型即可,錯誤參考代碼如下:
ChartMultiSeriesRenderData hntb = Charts
.ofMultiSeries("title", new String[] { "06-10","06-11","06-12","06-13","06-14","06-15"})
.addSeries("系列1", new Double[] { 70.5,40.6,22.7,85.4,700.0,40.8})
.addSeries("系列1", new Double[] { 80.5,50.6,62.7,45.4,200.0,140.8})
.addSeries("系列1", new Double[] { 120.5,520.6,362.7,405.4,300.0,340.8})
.create();正確參考代碼,通過series3.setComboType(SeriesRenderData.ComboType.LINE)指定:
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);2、圖表參數設置技巧
不同版本的office,對于參數的替換設置界面不一樣,尤其需要注意。這里提供兩種環(huán)境的設置參考。第一種是office2021。

office2021的圖表參數設置是鼠標點擊圖表右鍵,出現查看可選文字,在這里進行設置。

在以wps為例,本機安裝版本為:wps11.1.0

雙擊圖表空白區(qū),在文字選項中的大小與屬性一欄進行設置

總結
以上就是本文的主要內容,本文將簡單介紹poi-tl的相關知識,通過一個實際的案例實踐,充分介紹如何利用poi-tl進行目標文檔的生成,同時分享幾個不同的office版本如何進行圖表生成的解決方案。如果剛好您也在進行相關技術選型,本文可以作為使用參考。行文倉促,如有不當之處,歡迎批評指正。
poi-tl 參考技術網站:https://deepoove.com/poi-tl/
以上就是Java使用poi-tl1.9.1生成Word文檔的技巧分享的詳細內容,更多關于Java使用poi-tl1.9.1生成Word的資料請關注腳本之家其它相關文章!
相關文章
解讀httpclient的validateAfterInactivity連接池狀態(tài)檢測
這篇文章主要為大家介紹了httpclient的validateAfterInactivity連接池狀態(tài)檢測解讀*,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
詳解Java8新特性之interface中的static方法和default方法
這篇文章主要介紹了Java8新特性之interface中的static方法和default方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-08-08
Springboot項目使用html5的video標簽完成視頻播放功能
這篇文章主要介紹了Springboot項目使用html5的video標簽完成視頻播放功能,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12

