最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot3實現(xiàn)Word文檔動態(tài)生成與下載

 更新時間:2026年03月30日 09:24:52   作者:碼路星河  
日常開發(fā)中,經(jīng)常會遇到這樣的需求:根據(jù)業(yè)務(wù)數(shù)據(jù)動態(tài)生成Word文檔,比如合同導(dǎo)出、報表生成、用戶證明材料等,最近在SpringBoot3項目中,通過集成 poi-tl 工具,摸索出一套極簡高效的Word動態(tài)生成方案,需要的朋友可以參考下

引言

日常開發(fā)中,經(jīng)常會遇到這樣的需求:根據(jù)業(yè)務(wù)數(shù)據(jù)動態(tài)生成Word文檔,比如合同導(dǎo)出、報表生成、用戶證明材料等。如果直接用原生API操作Word,代碼繁瑣且容易出現(xiàn)格式錯亂,后期維護成本極高。

最近在SpringBoot3項目中,通過集成 poi-tl 工具,摸索出一套極簡高效的Word動態(tài)生成方案,無需復(fù)雜的樣式編碼配置,僅需簡單幾步,即可快速實現(xiàn)Word模板占位符填充、動態(tài)表格渲染及圖片插入等核心需求,大幅提升開發(fā)效率。

項目代碼結(jié)構(gòu)

先附上完整的項目代碼結(jié)構(gòu),方便大家對照搭建,后續(xù)所有代碼都將對應(yīng)此結(jié)構(gòu),避免路徑錯亂、類找不到等問題:

src
└── main
    ├── java
    │   └── com.example.demo
    │       ├── controller
    │       │   └── ContractController.java  <-- 接口類(接收請求、調(diào)用工具類)
    │       ├── dto
    │       │   ├── ContractDTO.java        <-- 主數(shù)據(jù)模型(對應(yīng)模板占位符)
    │       │   └── ContractDetailDTO.java  <-- 明細數(shù)據(jù)模型(對應(yīng)表格占位符)
    │       ├── util
    │       │   └── WordGenerateUtil.java   <-- 通用工具類(封裝生成、下載邏輯)
    │       └── DemoApplication.java        <-- 項目啟動類
    └── resources
        ├── static
        │   └── img
        │       └── attachment.jpg          <-- 測試圖片(用于圖片渲染驗證)
        ├── templates
        │   └── contractTemplate.docx       <-- Word模板(存放占位符)
        └── application.yml                 <-- 項目配置文件(默認配置即可)

一、環(huán)境準(zhǔn)備

  • JDK 17+;
  • Spring Boot 3.0+(本文用 3.2.5);
  • poi-tl 1.12.2;

1.1 引入Maven依賴

直接在pom.xml中添加以下依賴:

<!-- 核心依賴:實現(xiàn)Word模板渲染與生成 -->
<dependency>
    <groupId>com.deepoove</groupId>
    <artifactId>poi-tl</artifactId>
    <version>1.12.2</version> 
</dependency>
<!-- Apache POI: 處理Office文檔的核心庫 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.5.1</version>
</dependency>
<!-- 可選但推薦:文件操作工具 -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.21.0</version>
</dependency>

1.2 模板與圖片準(zhǔn)備

1. 模板準(zhǔn)備:在src/main/resources目錄下,新建templates文件夾,放入Word模板文件(后綴必須是.docx,不能是.doc,否則會報格式錯誤),命名為contractTemplate.docx;

2. 圖片準(zhǔn)備:在src/main/resources/static目錄下,新建img文件夾,放入一張測試圖片(命名為attachment.jpg),用于后續(xù)圖片渲染驗證。

二、實現(xiàn)Word動態(tài)生成與下載

整體流程:制作Word模板(設(shè)置占位符)→ 編寫數(shù)據(jù)模型(與占位符對應(yīng))→ 編寫工具類與接口(實現(xiàn)生成與下載)。

2.1 第一步:制作Word模板

模板制作的核心是設(shè)置“占位符”,后續(xù)代碼將數(shù)據(jù)替換到占位符中,且會完全繼承模板的原有樣式(字體、顏色、行距等),無需額外編寫樣式代碼。

制作規(guī)則(簡單好記,無需死記硬背):

  • 普通文本占位符:用 {{變量名}} 表示,比如 {{contractNo}}(合同編號)、{{customerName}}(客戶姓名);
  • 動態(tài)表格占位符:用 {{#表格變量名}} 開頭;
  • 圖片占位符:用 {{@圖片變量名}} 表示,后續(xù)通過代碼傳入圖片流即可正常渲染;
  • 占位符可以放在Word的任何位置(正文、表格、頁眉頁腳)。

實戰(zhàn)示例(以客戶合同模板為例):

打開WPS/Word,新建文檔,輸入以下內(nèi)容并插入占位符,保存為contractTemplate.docx,放入templates目錄:

客戶合同
合同編號:{{contractNo}}
客戶姓名:{{customerName}}
聯(lián)系電話:{{phone}}
簽訂日期:{{signDate}}
合同明細:
{{#detailList}}
合同附件:{{@attachmentImg}}

2.2 第二步:編寫數(shù)據(jù)模型

數(shù)據(jù)模型的作用是封裝需要填充到模板中的數(shù)據(jù),變量名必須和模板中的占位符完全一致(大小寫敏感)。

實戰(zhàn)代碼(兩個核心類,放在dto包下):

import lombok.Data;
import java.util.List;
import java.math.BigDecimal;
/**
 * 合同主數(shù)據(jù)模型(對應(yīng)模板中的普通文本占位符)
 */
@Data
public class ContractDTO {
    // 合同編號(對應(yīng){{contractNo}})
    private String contractNo;
    // 客戶姓名(對應(yīng){{customerName}})
    private String customerName;
    // 聯(lián)系電話(對應(yīng){{phone}})
    private String phone;
    // 簽訂日期(對應(yīng){{signDate}})
    private String signDate;
    // 合同明細(對應(yīng){{#detailList}}循環(huán)表格)
    private List<ContractDetailDTO> detailList;
    // 附件圖片(對應(yīng){{@attachmentImg}},無需賦值,接口中單獨處理)
    private String attachmentImg;
}
/**
 * 合同明細數(shù)據(jù)模型(對應(yīng)表格中的占位符)
 */
@Data
public class ContractDetailDTO {
    // 商品名稱(對應(yīng){{productName}})
    private String productName;
    // 單價(對應(yīng){{price}})
    private BigDecimal price;
    // 數(shù)量(對應(yīng){{num}})
    private Integer num;
    // 小計(對應(yīng){{total}})
    private BigDecimal total;
}

2.3 編寫通用Word工具類

工具類封裝了“生成Word并下載”“生成Word保存到本地”兩個核心方法。

import com.deepoove.poi.XWPFTemplate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;
/**
 * Word文檔生成工具類(通用,可直接復(fù)用)
 */
@Component
public class WordGenerateUtil {
    /**
     * 生成Word并通過瀏覽器下載
     * @param templateName 模板文件名(放在resources/templates目錄下)
     * @param data 填充到模板的數(shù)據(jù)(Map格式,key對應(yīng)模板占位符)
     * @param response 響應(yīng)對象(用于返回下載流)
     * @param downloadFileName 下載時的文件名(如:張三的合同.docx)
     * @throws IOException 異常(可在調(diào)用處統(tǒng)一處理)
     */
    public void generateAndDownload(String templateName, Map<String, Object> data,
                                    HttpServletResponse response, String downloadFileName) throws IOException {
        // 1. 讀取templates目錄下的Word模板
        ClassPathResource resource = new ClassPathResource("templates/" + templateName);
        // 2. 編譯模板并填充數(shù)據(jù)
        XWPFTemplate template = XWPFTemplate.compile(resource.getInputStream()).render(data);
        // 3. 設(shè)置響應(yīng)頭,實現(xiàn)瀏覽器下載(解決中文文件名亂碼問題)
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(downloadFileName, "UTF-8"));
        response.setCharacterEncoding("UTF-8");
        // 4. 寫入響應(yīng)流,完成下載
        try (OutputStream outputStream = response.getOutputStream()) {
            template.write(outputStream);
            outputStream.flush();
        } finally {
            // 5. 關(guān)閉資源,避免內(nèi)存泄漏
            template.close();
        }
    }
    /**
     * 生成Word并保存到本地(可選,根據(jù)需求使用)
     * @param templateName 模板文件名
     * @param data 填充數(shù)據(jù)
     * @param localPath 本地保存路徑(如:D:/contract/張三的合同.docx)
     * @throws IOException 異常
     */
    public void generateToLocal(String templateName, Map<String, Object> data, String localPath) throws IOException {
        ClassPathResource resource = new ClassPathResource("templates/" + templateName);
        XWPFTemplate template = XWPFTemplate.compile(resource.getInputStream()).render(data);
        // 寫入本地文件
        template.writeToFile(localPath);
        template.close();
    }
}

2.4 編寫接口類

編寫REST接口,模擬從數(shù)據(jù)庫獲取數(shù)據(jù)(實際開發(fā)中替換為真實DAO查詢),調(diào)用工具類實現(xiàn)Word下載。

import cn.iocoder.boot.entity.ContractDTO;
import cn.iocoder.boot.entity.ContractDetailDTO;
import cn.iocoder.boot.utils.WordGenerateUtil;
import com.deepoove.poi.data.*;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
/**
 * 合同導(dǎo)出接口(實戰(zhàn)示例)
 */
@RestController
@RequestMapping("/contract")
public class ContractController {
    @Resource
    private WordGenerateUtil wordGenerateUtil;
    /**
     * 導(dǎo)出單個客戶合同
     * @param customerId 客戶ID(實際開發(fā)中用于查詢客戶數(shù)據(jù))
     * @param response 響應(yīng)對象(返回下載流)
     * @throws IOException 異常
     */
    @GetMapping("/export/{customerId}")
    public void exportContract(@PathVariable String customerId, HttpServletResponse response) throws IOException {
        // 1. 模擬從數(shù)據(jù)庫查詢客戶合同數(shù)據(jù)(實際開發(fā)中替換為真實DAO查詢)
        ContractDTO contractDTO = getContractData(customerId);
        // 2. 組裝數(shù)據(jù)(key必須和模板占位符完全一致)
        Map<String, Object> data = new HashMap<>();
        data.put("contractNo", contractDTO.getContractNo());
        data.put("customerName", contractDTO.getCustomerName());
        data.put("phone", contractDTO.getPhone());
        data.put("signDate", contractDTO.getSignDate());
        // 3. 創(chuàng)建表格數(shù)據(jù)
        RowRenderData row0 = Rows.of("商品名稱", "單價(元)","數(shù)量","小計(元)").textColor("FFFFFF")
                .bgColor("4472C4").center().create();
        Tables.TableBuilder tableBuilder = Tables.of(row0);
        contractDTO.getDetailList().forEach(detail -> {
            RowRenderData row = Rows.create(detail.getProductName(), detail.getPrice().toString(), detail.getNum().toString(), detail.getTotal().toString());
            tableBuilder.addRow(row);
        });
        data.put("detailList", tableBuilder.create());
        // 4. 填充圖片,圖片路徑:項目resources/static/img目錄下的圖片(實際可從數(shù)據(jù)庫獲取圖片路徑)
        data.put("attachmentImg", Pictures.ofStream(
                        new ClassPathResource("static/img/attachment.jpg").getInputStream(), // 圖片流
                        PictureType.JPEG) // 圖片格式,無需手動寫后綴
                .size(200, 100) // 圖片寬高(單位:像素)
                .create()
        );
        // 5. 調(diào)用工具類,生成并下載Word
        wordGenerateUtil.generateAndDownload(
                "contractTemplate.docx", // 模板文件名
                data, // 填充數(shù)據(jù)
                response, // 響應(yīng)對象
                contractDTO.getCustomerName() + "的合同.docx" // 下載文件名
        );
    }
    /**
     * 模擬查詢合同數(shù)據(jù)(實際開發(fā)中替換為真實業(yè)務(wù)邏輯/DAO查詢)
     */
    private ContractDTO getContractData(String customerId) {
        ContractDTO contract = new ContractDTO();
        // 模擬主數(shù)據(jù)(實際從數(shù)據(jù)庫查詢)
        contract.setContractNo("HT-" + System.currentTimeMillis());
        contract.setCustomerName("張三");
        contract.setPhone("13800138000");
        contract.setSignDate("2026-03-25");
        // 模擬合同明細數(shù)據(jù)(對應(yīng)表格循環(huán))
        List<ContractDetailDTO> detailList = new ArrayList<>();
        ContractDetailDTO detail1 = new ContractDetailDTO();
        detail1.setProductName("Java開發(fā)服務(wù)");
        detail1.setPrice(new BigDecimal("5000.00"));
        detail1.setNum(1);
        detail1.setTotal(new BigDecimal("5000.00"));
        ContractDetailDTO detail2 = new ContractDetailDTO();
        detail2.setProductName("系統(tǒng)維護服務(wù)");
        detail2.setPrice(new BigDecimal("2000.00"));
        detail2.setNum(1);
        detail2.setTotal(new BigDecimal("2000.00"));
        detailList.add(detail1);
        detailList.add(detail2);
        contract.setDetailList(detailList);
        return contract;
    }
}

三、測試驗證

測試步驟簡單,無需復(fù)雜配置,啟動SpringBoot項目后,直接訪問接口即可驗證功能是否正常。

3.1 前置準(zhǔn)備

1. 確認templates目錄下有contractTemplate.docx模板,static/img目錄下有attachment.jpg圖片;

2. 確保項目啟動無報錯(JDK17環(huán)境,依賴正常引入);

3. 無需修改application.yml,默認配置即可。

3.2 接口訪問與驗證

訪問接口地址:http://localhost:8080/contract/export/1(customerId隨便傳,此處僅為模擬),瀏覽器會自動下載Word文件。

四、常見問題

  1. 模板后綴必須是.docx,不能是.doc,否則會報“不支持的格式”異常;
  2. 占位符大小寫敏感,比如模板中是{{contractNo}},代碼中寫contractno會導(dǎo)致填充失??;
  3. 圖片渲染需通過Pictures工具類創(chuàng)建渲染對象,僅傳字符串路徑會導(dǎo)致圖片無法顯示;項目內(nèi)圖片用ClassPathResource獲取流,本地圖片用FileInputStream獲取流;
  4. 批量生成Word時,必須循環(huán)關(guān)閉template資源,否則會導(dǎo)致內(nèi)存溢出;

五、擴展場景

實際開發(fā)中,除了基礎(chǔ)的文本、表格、圖片填充,還可能遇到以下場景,簡單補充實現(xiàn)思路:

  • 條件渲染:某些字段為空時不顯示,可使用{{?變量名}} 占位符(如{{?remark}} 備注:{{remark}} {{/?remark}});
  • 動態(tài)圖片:從數(shù)據(jù)庫獲取圖片流(無需保存到本地),直接傳入Pictures.ofStream()方法即可渲染;
  • 批量導(dǎo)出:循環(huán)調(diào)用工具類的generateToLocal方法,生成多個Word文件,再通過ZipOutputStream打包成zip,返回給前端下載。

六、總結(jié)

本次實戰(zhàn)用SpringBoot3實現(xiàn)Word動態(tài)生成與下載,核心邏輯是“模板占位符+數(shù)據(jù)模型+通用工具類”,無需復(fù)雜的樣式配置,所有代碼均可直接復(fù)制復(fù)用。

對比原生API,這種方式不僅代碼簡潔,而且后期維護方便——修改模板無需改代碼,只需調(diào)整Word文件中的占位符和樣式即可,極大降低維護成本。

以上就是SpringBoot3實現(xiàn)Word文檔動態(tài)生成與下載的詳細內(nèi)容,更多關(guān)于SpringBoot3 Word動態(tài)生成與下載的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot中使用Filter和Interceptor的示例代碼

    SpringBoot中使用Filter和Interceptor的示例代碼

    這篇文章主要介紹了SpringBoot中使用Filter和Interceptor的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • 基于RestTemplate的使用方法(詳解)

    基于RestTemplate的使用方法(詳解)

    下面小編就為大家?guī)硪黄赗estTemplate的使用方法(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • Spring?緩存在項目中的使用詳解

    Spring?緩存在項目中的使用詳解

    Spring?緩存機制,Cache接口為緩存的組件規(guī)范定義,包擴緩存的各種操作(添加緩存、刪除緩存、修改緩存等),本文給大家介紹Spring?緩存在項目中的使用,感興趣的朋友一起看看吧
    2025-05-05
  • Java設(shè)計模式之策略模式詳細解析

    Java設(shè)計模式之策略模式詳細解析

    這篇文章主要介紹了Java設(shè)計模式之策略模式詳細解析,策略模式中,定義算法族,分別封裝起來,讓他們之間可以相互轉(zhuǎn)化,此模式讓算法的變化獨立于使用算法的客戶,需要的朋友可以參考下
    2023-11-11
  • Java RandomAccessFile的用法詳解

    Java RandomAccessFile的用法詳解

    下面小編就為大家?guī)硪黄狫ava RandomAccessFile的用法詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • java實現(xiàn)學(xué)生信息錄入界面

    java實現(xiàn)學(xué)生信息錄入界面

    這篇文章主要為大家詳細介紹了java實現(xiàn)學(xué)生信息錄入界面,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 解決使用json-lib包實現(xiàn)xml轉(zhuǎn)json時空值被轉(zhuǎn)為空中括號的問題

    解決使用json-lib包實現(xiàn)xml轉(zhuǎn)json時空值被轉(zhuǎn)為空中括號的問題

    網(wǎng)上能查到的xml轉(zhuǎn)json的jar包大部分是net.sf.json-lib,但是JSON json =xmlSerializer.read(xml); 方法會出現(xiàn)將空值轉(zhuǎn)化為[]的問題,下面為大家提供兩種解決方法
    2018-03-03
  • SpringBoot 使用 OpenAPI3 規(guī)范整合 knife4j的詳細過程

    SpringBoot 使用 OpenAPI3 規(guī)范整合 knife4j的詳細過程

    Swagger工具集使用OpenAPI規(guī)范,可以生成、展示和測試基于OpenAPI規(guī)范的API文檔,并提供了生成客戶端代碼的功能,本文給大家介紹SpringBoot使用OpenAPI3規(guī)范整合knife4j的詳細過程,感興趣的朋友跟隨小編一起看看吧
    2023-12-12
  • Spring注解和同步鎖不能同步問題解決

    Spring注解和同步鎖不能同步問題解決

    這篇文章主要介紹了Spring注解和同步鎖不能同步問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • 使用Cloud Toolkit在IDEA中極速創(chuàng)建dubbo工程

    使用Cloud Toolkit在IDEA中極速創(chuàng)建dubbo工程

    這篇文章主要介紹了使用Cloud Toolkit在IDEA中極速創(chuàng)建dubbo工程,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評論

邯郸市| 金溪县| 若尔盖县| 波密县| 兰溪市| 大埔区| 宁河县| 昌平区| 衡阳市| 肥乡县| 化德县| 双鸭山市| 田东县| 潞城市| 晋江市| 炎陵县| 彭阳县| 林西县| 南丹县| 九江县| 股票| 梓潼县| 凉山| 汾阳市| 余庆县| 佛冈县| 甘肃省| 玉门市| 榆林市| 铜鼓县| 两当县| 武隆县| 柘荣县| 茌平县| 泾阳县| 乐昌市| 镇赉县| 岳池县| 顺昌县| 谢通门县| 前郭尔|