Spring Boot 中使用 Poi-tl 渲染數據並生成 Word 文檔

来源:https://www.cnblogs.com/god23bin/archive/2023/09/10/spring-boot-poi-tl.html
-Advertisement-
Play Games

項目中有這麼一個需求,需要將用戶填寫的數據填充到一個 Word 文檔中,而這個 Word 文檔是人家給定了的。換句話說,讓你按照這個文檔的內容格式生成新的文檔。 ...


本文 Demo 已收錄到 demo-for-all-in-java 項目中,歡迎大家 star 支持!後續將持續更新!

前言

產品經理急衝衝地走了過來。「現在需要將按這些數據生成一個 Word 報告文檔,你來安排下」

項目中有這麼一個需求,需要將用戶填寫的數據填充到一個 Word 文檔中,而這個 Word 文檔是人家給定了的。換句話說,讓你按照這個文檔的內容格式生成新的文檔。

什麼是 Poi-tl ?

官網:http://deepoove.com/poi-tl/1.9.x/

poi-tl(poi template language)是一種 Word 模板引擎,可以基於 Word 模板和數據生成新的文檔,它的底層是通過 Apache POI 來實現的。

Apache POI 不僅封裝了易用的文檔 API (文本、圖片、表格、頁眉、頁腳、圖表等),也可以在底層直接操作文檔XML結構。

poi-tl 擁有如下特性(瞭解瞄一眼就行):

內容 描述
文本 將標簽渲染為文本
圖片 將標簽渲染為圖片
表格 將標簽渲染為表格
列表 將標簽渲染為列表
圖表 條形圖(3D條形圖)、柱形圖(3D柱形圖)、面積圖(3D面積圖)、折線圖(3D折線圖)、雷達圖、餅圖(3D餅圖)等圖表渲染
If Condition判斷 隱藏或者顯示某些文檔內容(包括文本、段落、圖片、表格、列表、圖表等)
Foreach Loop迴圈 迴圈某些文檔內容(包括文本、段落、圖片、表格、列表、圖表等)
Loop表格行 迴圈渲染表格的某一行
Loop表格列 迴圈渲染表格的某一列
Loop有序列表 支持有序列表的迴圈,同時支持多級列表
圖片替換 將原有圖片替換成另一張圖片
書簽、錨點、超鏈接 支持設置書簽,文檔內錨點和超鏈接功能
強大的表達式 完全支持SpringEL表達式,可以擴展更多的表達式:OGNL, MVEL…
標簽定製 支持自定義標簽前尾碼
文本框 文本框內標簽支持
樣式 模板即樣式,同時代碼也可以設置樣式
模板嵌套 模板包含子模板,子模板再包含子模板
合併 Word合併Merge,也可以在指定位置進行合併
用戶自定義函數(插件) 在文檔任何位置執行函數

我們就可以使用這個它來實現這個需求。

如何使用 Poi-tl ?

本篇文章將以 Spring Boot 項目作為演示,屏幕前的朋友們可以一起跟著我的步驟來,實踐一番!

  1. 首先創建一個 Spring Boot 項目,版本目前我的 Demo 是 2.2.1,你可以更改你的 Spring Boot 版本,那現在我這裡已經創建好了。

其中, pom.xml 只有兩個依賴項,一個 web 和一個 test :

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Test 依賴 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
  1. 接著在 pom.xml 中引入 Poi-tl 的依賴項
<!-- Poi-tl Word 模板引擎-->
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl</artifactId>
    <version>1.9.1</version>
</dependency>
  1. 準備一個 Word 模板

這一步你可以自己動手做一個 Word 模板,這裡我先演示下。就先創建一個名為 Hello World.docx 的 Word 文檔,模板內容如下:

找一個你喜歡的位置存放這個模板,我現在把它放到項目的 resource 目錄。

{{title}} 這種由兩個大括弧包住的,目前可以看成占位符,這個模板中有 4 個占位符,後續的數據就渲染到這些地方上。

  1. 獲取模板所在的路徑,並將數據渲染到模板上

渲染只需一行代碼,就是使用 XWPFTemplate 的 API 就可以了,通過 complierender 方法,就可以將數據渲染到模板中,得到渲染好的新文檔。

@SpringBootTest
public class PoiTlApplicationTest {

    @Test
    public void test() {
        // 獲取 Word 模板所在路徑
        String filepath = this.getClass().getClassLoader().getResource("hello-world.docx").getPath();
        // 通過 XWPFTemplate 編譯文件並渲染數據到模板中
        XWPFTemplate template = XWPFTemplate.compile(filepath).render(
                new HashMap<String, Object>(){{
                    put("title", "Hello, poi-tl Word模板引擎");
                    put("text", "Hello World");
                    put("author", "god23bin");
                    put("description", "這還不關註 god23bin ?再不關註我可要求你關註了!");
                }});
        try {
            // 將完成數據渲染的文檔寫出
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
}

執行這個單元測試,就可以看到在項目所在目錄下輸出了新的文檔 output.docx,打開這個文檔,我們就可以看到如下圖所示的內容:

大功告成,這就是渲染好的新文檔了,是不是很簡單,一行代碼就完成了根據模板進行數據的渲染!

相關概念

模板

模板是 Docx 格式的 Word 文檔,我們可以使用 Microsoft office、WPS Office 等軟體來製作模板。

標簽

上面說到 {{title}} 這種理解成占位符,實際上,官方是稱之為「標簽」。

所有的標簽都是以 {{ 開頭,以 }} 結尾,標簽可以出現在任何位置,包括頁眉,頁腳,表格內部,文本框等。

表格佈局可以設計出很多優秀專業的文檔,推薦使用表格佈局。

poi-tl 模板遵循 所見即所得 的設計,模板和標簽的樣式會被完全保留,就如我上面演示的,一級標題和字體顏色的樣式就被保留下來了。

數據模型

數據模型,也就是我們需要渲染到模板中的數據,可以是哈希表,也可以是普通的 Java 對象。

  1. 哈希表(key 名是標簽名):
Map<String, Object> data = new HashMap<>();
data.put("title", "Hello, poi-tl Word模板引擎");
data.put("text", "Hello World");
data.put("author", "god23bin");
data.put("description", "這還不關註 god23bin ?再不關註我可要求你關註了!");
  1. Java 對象(屬性名是標簽名):
public class DataModel {
    private String title;
    private String text;
    private String author;
    private String description;
    // 省略 getter 和 setter
}

DataModel data = new DataModel();
data.setTitle("Hello, poi-tl Word模板引擎");
data.setText("Hello World");
data.setAuthor("god23bin");
data.setDescription("這還不關註 god23bin ?再不關註我可要求你關註了!");

有了哈希表和或者 Java 對象的數據模型後,將這個數據丟給渲染的 API,就可以完成數據的渲染了。

標簽的寫法

poi-tl 里只有標簽,那麼我們需要知道標簽的寫法是怎樣的。在 Word 文檔里,可以有:文本、圖片、表格、列表等元素,那麼對應的,咱們的標簽也有這些。

文本標簽 {{var}}

簡單粗暴,直接 {{標簽名}} 就是文本標簽了。

圖片標簽 {{@var}}

{{@標簽名}} 就是圖片標簽,@ 標識了這個標簽的類型是圖片,其他的也是同理,不同的符號標識不同類型的標簽。

表格標簽 {{#var}}

使用 # 標識這是一個表格標簽。

列表標簽 {{*var}}

使用 * 標識這是一個列表標簽。

其餘的標簽

剩下的標簽還有很多,詳細的內容你可以閱讀官方文檔,這裡就不一一介紹了。

下麵我將寫下我用過的內容。

插件

插件,又稱為自定義函數,它允許我們在模板標簽位置處執行預先定義好的函數。由於插件機制的存在,我們幾乎可以在模板的任何位置執行任意操作。

插件是 poi-tl 的核心,預設的標簽和引用標簽都是通過插件載入。

預設插件

poi-tl 預設提供了八個策略插件,用來處理文本、圖片、列表、表格、文檔嵌套、引用圖片、引用多系列圖表、引用單系列圖表等:

  • TextRenderPolicy
  • PictureRenderPolicy
  • NumberingRenderPolicy
  • TableRenderPolicy
  • DocxRenderPolicy
  • MultiSeriesChartTemplateRenderPolicy
  • SingleSeriesChartTemplateRenderPolicy
  • DefaultPictureTemplateRenderPolicy

由於這 8 個插件是經常用到的,所以這些插件被註冊為不同的標簽類型,也就是我們看到過的 {{var}}、{{@var}}、{{#var}} 等不同類型的標簽,從而搭建了 poi-tl 的標簽體系。

除了這 8 個通用的策略插件外,還內置了一些額外用途的插件:

DynamicTableRenderPolicy 動態表格插件,允許直接操作表格對象 示例-動態表格
HackLoopTableRenderPolicy 迴圈表格行,下文會詳細介紹 示例-表格行迴圈
LoopColumnTableRenderPolicy 迴圈表格列 示例-表格列迴圈
BookmarkRenderPolicy 書簽和錨點 示例-Swagger文檔
JSONRenderPolicy 高亮顯示JSON代碼塊 示例-Swagger文檔
AbstractChartTemplateRenderPolicy 引用圖表插件,允許直接操作圖表對象
ParagraphRenderPolicy 渲染一個段落,可以包含不同樣式文本,圖片等
DocumentRenderPolicy 渲染多個段落和表格
TOCRenderPolicy Beta實驗功能:目錄,打開文檔時需要更新域

使用插件

為了讓插件在某個標簽處執行,我們需要將插件與標簽綁定

當我們有個模板標簽為 {{description}},預設是文本標簽,如果希望在這個位置做些不一樣或者更複雜的事情,我們可以將插件應用到這個模板標簽,比如渲染 HTML:

ConfigureBuilder builder = Configure.builder();
builder.bind("description", new HtmlRenderPolicy());

此時,{{description}} 將不再是一個文本標簽,而是一個自定義的支持 HTML 渲染的標簽。

當然,這裡的 HTML 渲染的插件,預設是沒有提供的,需要引入以下的依賴項,才能支持 HTML 的渲染。

<!-- 支持渲染 HTML 的插件 -->
<dependency>
    <groupId>io.github.draco1023</groupId>
    <artifactId>poi-tl-ext</artifactId>
    <version>0.3.3</version>
</dependency>

示例:我們對 {{author}} 這個標簽綁定上支持 HTML 渲染的插件,這樣就能渲染 HTML 的文本了。

@SpringBootTest
public class PoiTlApplicationTest {

    @Test
    public void test() {
        // 獲取 Word 模板所在路徑
        String filepath = this.getClass().getClassLoader().getResource("hello-world.docx").getPath();
        // 給標簽綁定插件
        Configure configure = Configure.builder().bind("author", new HtmlRenderPolicy()).build();
        // 通過 XWPFTemplate 編譯文件並渲染數據到模板中
        XWPFTemplate template = XWPFTemplate.compile(filepath, configure).render(
                new HashMap<String, Object>(){{
                    put("title", "Hello, poi-tl Word模板引擎");
                    put("text", "Hello World");
                    put("author", "<h2>god23bin</h2>");
                    put("description", "這還不關註 god23bin ?再不關註我可要求你關註了!");
                }});
        try {
            // 將完成數據渲染的文檔寫出
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
}

生成的 Word 文檔如下圖所示,可以看到 {{author}} 這個標簽的 HTML 文本已經渲染成功了!(藍框框)

表格行迴圈

當有類似如下需求的時候,在表格裡展示多行同類型的數據,那麼就需要用到表格的行迴圈了。

這裡也是涉及到插件的,具體就是使用 HackLoopTableRenderPolicy 這個插件策略,這個策略能夠根據集合數據進行迴圈渲染,這樣就渲染數據到表格的行上了,集合有多少個元素,那麼就有多少行。

我們來看下這個表格行迴圈的模板是怎樣寫的,是這樣的:

可以看到 {{articles}}{{columns}} 是標準的文本標簽,我們這裡將這兩個標簽置於迴圈行的上一行,迴圈行里設置要迴圈的標簽和內容,註意這裡的標簽是使用 [] 的,以此來區分標準的標簽語法。

同時 {{articles}}{{columns}} 標簽對應的數據就是文章和專欄的集合。

我們寫一個該模板的數據模型,以 Java 對象來寫,同時模擬數據從資料庫中讀取。

數據模型:AcWordModel

public class AcWordModel {
    /**
     * 文章明細數據模型-表格行迴圈
     */
    private List<Article> articles;
    /**
     * 專欄明細數據模型
     */
    private List<SpecialColumn> columns;
    // 省略 getter 和 setter
}

其中的 Article 和 SpecialColumn 模型如下:

public class Article {
    private String title;
    private String tags;
    private Integer reading;
    private Integer likes;
    // 省略 getter 和 setter
}
public class SpecialColumn {
    private String name;
    private Integer subscription;
    private Integer nums;
    // 省略 getter 和 setter
}

進行測試,獲取數據和模板,讓標簽和表格行迴圈的插件進行綁定

    @Test
    public void rowLoopTest() {
        // 獲取數據,這裡假裝是從資料庫中查詢得到的
        AcWordModel data = getFromDB();
        // 獲取 Word 模板所在路徑
        String filepath = this.getClass().getClassLoader().getResource("table-row-loop.docx").getPath();
        // 給標簽綁定插件,這裡就綁定表格行迴圈的插件
        Configure configure = Configure.builder()
                .bind("articles", new HackLoopTableRenderPolicy())
                .bind("columns", new HackLoopTableRenderPolicy())
                .build();
        // 通過 XWPFTemplate 編譯文件並渲染數據到模板中
        XWPFTemplate template = XWPFTemplate.compile(filepath, configure).render(data);
        try {
            // 將完成數據渲染的文檔寫出
            template.writeAndClose(new FileOutputStream("ac-word.docx"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

這樣,就能實現表格的行迴圈了!

封裝 Word 渲染生成新文檔的工具

我們可以再封裝下這個 API,寫一個工具類,如下:

package cn.god23bin.demo.util;

import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;

public class WordUtil {

    /**
     * 生成 word 文檔
     * @param wordTemplatePath    word 模板路徑
     * @param targetWordFilePath  生成目標文檔路徑
     * @param data                待渲染的數據模型-哈希表形式
     */
    public static void generateWordFile(String wordTemplatePath, String targetWordFilePath, Map<String, Object> data) {
        XWPFTemplate template = XWPFTemplate.compile(wordTemplatePath).render(data);
        try {
            template.writeAndClose(new FileOutputStream(targetWordFilePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成 word 文檔
     * @param wordTemplatePath    word 模板路徑
     * @param targetWordFilePath  生成目標文檔路徑
     * @param data                待渲染的數據模型-Java對象形式
     */
    public static void generateWordFile(String wordTemplatePath, String targetWordFilePath, Object data) {
        XWPFTemplate template = XWPFTemplate.compile(wordTemplatePath).render(data);
        try {
            template.writeAndClose(new FileOutputStream(targetWordFilePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成 word 文檔
     * @param wordTemplatePath    word 模板路徑
     * @param targetWordFilePath  生成目標文檔路徑
     * @param data                待渲染的數據模型-哈希表形式
     * @param configure           渲染配置
     */
    public static void generateWordFile(String wordTemplatePath, String targetWordFilePath, Map<String, Object> data, Configure configure) {
        XWPFTemplate template = XWPFTemplate.compile(wordTemplatePath, configure).render(data);
        try {
            template.writeAndClose(new FileOutputStream(targetWordFilePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成 word 文檔
     * @param wordTemplatePath    word 模板路徑
     * @param targetWordFilePath  生成目標文檔路徑
     * @param data                待渲染的數據模型-Java對象形式
     * @param configure           渲染配置
     */
    public static void generateWordFile(String wordTemplatePath, String targetWordFilePath, Object data, Configure configure) {
        XWPFTemplate template = XWPFTemplate.compile(wordTemplatePath, configure).render(data);
        try {
            template.writeAndClose(new FileOutputStream(targetWordFilePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

最後的最後

希望各位屏幕前的靚仔靚女們給個三連!你輕輕地點了個贊,那將在我的心裡世界增添一顆明亮而耀眼的星!

咱們下期再見!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 溫馨提示:本文以vue3+vite+ts舉例,vite配置和ts語法側重較少,比較適合有vuex或者vue基礎的小伙伴們兒查閱。 安裝pinia yarn yarn add pinia npm npm install pinia pnpm pnpm add pinia 1-開始 方式一:在main. ...
  • 官方代碼是直接使用JDK的Deque對象,這樣的代碼能學到什麼?熟練操作API嗎?還是自己實現一個最小棧吧,用時擊敗100%,記憶體擊敗78% ...
  • 在您的應用程式中,由Spring IoC容器管理的形成其核心的對象被稱為"bean"。一個bean是由Spring IoC容器實例化、組裝和管理的對象。這些bean是通過您提供給容器的配置元數據創建的,例如,在前面章節中已經看到的XML <bean/> 定義。 Bean定義包含了所謂的配置元數據,容 ...
  • 因為Docker Hub無法打開,Jupyter Notebook等官方鏡像也無法下載,所以另闢蹊徑下載了熱門的Python3基礎鏡像,從頭開始安裝純凈版本的Jupyter Notebook環境,本文記錄了完整的Jupyter Notebook安裝過程,方便自己查閱,也供其他人員參考,請確保當前已有 ...
  • 1 Nacos ⼀致性協議 1.1 為什麼 Nacos 需要⼀致性協議 Nacos儘可能減少用戶部署以及運維成本,做到用戶只需要⼀個程式包,就快速單機模式啟動 Nacos 或集群模式啟動 Nacos。而 Nacos 是⼀個需要存儲數據的組件,為實現目標,就要在 Nacos 內部實現數據存儲。單機問題 ...
  • 在定位公司問題的時候,需要瞭解一下skywalking的相關知識,而agent就提上了日程。 官網文檔 Agent技術是Jdk在1.5版本之後,所提供的一個在jvm啟動前後對部分java類代理加強的機制。由於是直接修改位元組碼,並不會對業務代碼有註入,所以可以很好的應用於監控或者熱部署等場景。 正常所 ...
  • Matplotlib 文本和標註可以為數據和圖形之間提供額外的信息,幫助觀察者更好地理解數據和圖形的含義。 文本用於在圖形中添加註釋或提供更詳細的信息,以幫助觀察者理解圖形的含義。標註則是一種更加細粒度的文本信息,可以被用來為特定的數據點或區域提供更詳細的信息。 本篇通過示例依次介紹文本和標註的常用 ...
  • 大家好,又到了求職季,給大家分享一段一位網友的央企工作經歷: 來源:zhihu.com/question/276681361/answer/2134441878 希望對於觀望工作機會的小伙伴,有些參考~ 我校招加入了某壟斷央企,在裡面從事研發工程師的工作。下麵我將分享一些入職後的一些心得體會。在國企 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...