欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java通過freemarker生成Word文檔導(dǎo)出的方式詳解

 更新時(shí)間:2025年03月04日 10:48:40   作者:Dawn  
本文詳細(xì)介紹了如何使用FreeMarker模板生成Word文檔,包括制作FTL模板、在Java中使用FreeMarker生成文檔以及處理動(dòng)態(tài)數(shù)據(jù)和合并單元格等內(nèi)容,需要的朋友可以參考下

1.制作ftl模板

在Word中制作模板,制作完后以XML格式導(dǎo)出

 新建WORD文件,將所需參數(shù)用特殊符號(hào) ${} 配置,如下圖:

注意:freemarker中的占位符是${},例如:這里使用的是【${name}】的形式,那么傳遞的數(shù)據(jù)中就需要有一個(gè)叫做【name】的字段。

導(dǎo)出時(shí)選擇.xml格式導(dǎo)出

用編譯器(notepad++)打開看一下動(dòng)態(tài)參數(shù)是否正確,并修改后綴名為ftl

導(dǎo)出XML文件之后,打開這個(gè)文件,此時(shí)你會(huì)看到里面都是XML標(biāo)簽,首先格式化一下,這樣看起來會(huì)舒服些,可以檢查一下你的占位符內(nèi)容是否滿足freemarker語法。因?yàn)橛行r(shí)候,我們導(dǎo)出的XML文件中,可能會(huì)將【${xxx}】分隔成兩行,從而導(dǎo)致占位符失效,所以有時(shí)候需要手動(dòng)修改一下占位符。導(dǎo)出的Word XML文件內(nèi)容大致如下所示:

2.pom文件引入freemarke依賴,安裝依賴

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.30</version>
        </dependency>

3.java代碼實(shí)現(xiàn)

3.1 將ftl模板移入項(xiàng)目中

3.2  創(chuàng)建Freemarker工具類

引入freemarker依賴之后,就可以使用Freemarker編寫一個(gè)工具類,專門用于處理文件的導(dǎo)出和數(shù)據(jù)渲染。

import freemarker.template.Configuration;
import freemarker.template.Template;
 
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.util.Map;
 
/**
 * @version 1.0.0
 * @Date: 2025/2/14 15:05
 * @Author SYG
 * @Description: Freemarker 工具類
 */
public class FreemarkerUtil {
 
    /**
     * 使用 Freemarker 生成 Word 文件并導(dǎo)出
     * @param templateName 模板文件路徑名稱
     * @param fileName 生成的文件路徑以及名稱
     * @param dataModel 填充的數(shù)據(jù)對(duì)象
     * @param response HttpServletResponse 對(duì)象
     */
    public static void exportWord(String templateName, String fileName, Map<String, Object> dataModel, Object obj, HttpServletResponse response) {
        generateFile(templateName, fileName, dataModel, obj, response);
    }
 
    /**
     * 使用 Freemarker 生成指定文件并導(dǎo)出
     * @param templateName 模板文件路徑名稱
     * @param fileName 生成的文件路徑以及名稱
     * @param dataModel 填充的數(shù)據(jù)對(duì)象
     * @param obj 基準(zhǔn)類對(duì)象
     * @param response HttpServletResponse 對(duì)象
     */
    private static void generateFile(String templateName, String fileName, Map<String, Object> dataModel, Object obj, HttpServletResponse response) {
        try {
            // 1、創(chuàng)建配置對(duì)象
            Configuration config = new Configuration(Configuration.VERSION_2_3_30);
            config.setDefaultEncoding("utf-8");
            //設(shè)置了模板文件的加載類和路徑,使得Freemarker可以從指定的類路徑下的/templates目錄中加載模板文件。
            config.setClassForTemplateLoading(obj.getClass(), "/templates");
            // 2、獲取模板文件
            Template template = config.getTemplate(templateName);
            // 3、設(shè)置響應(yīng)頭
            response.setContentType("application/msword");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            // 4、創(chuàng)建寫入器
            Writer writer = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));
            // 5、渲染模板文件
            template.process(dataModel, writer);
            // 6、關(guān)閉寫入器
            writer.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
}

3.3 測(cè)試案例代碼

 
public class ExportWordDemo {
    public static void main(String[] args) {
        String templateName = "opinionCollect.ftl";
        String fileName = "opinionCollect.docx";
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("orgName", "測(cè)試機(jī)構(gòu)02141151");
        dataModel.put("data", "2025-02-14");
        // 執(zhí)行導(dǎo)出
        FreemarkerUtil.exportWord(templateName, fileName, dataModel,this,response);
    }
}

案例所使用的ftl文件共有兩個(gè)變量data和orgName,若map集合里只給data或orgName一個(gè)變量賦值(例如我們只給data賦值        

dataModel.put("data", "2025-02-14");
),則會(huì)報(bào)錯(cuò):

FreeMarker template error:
The following has evaluated to null or missing:

==> orgName [in template "payment.ftl" at line 3, column 23752]

為解決當(dāng)前異常,這里有倆種方案:

  • 給orgName賦一個(gè)空值,即dataModel.put("orgName", ""); 
  • (推薦)修改模板,根據(jù)數(shù)據(jù)結(jié)構(gòu)字段,將需要填充數(shù)據(jù)的地方,修改為EL表達(dá)式。${()!} 加括號(hào)!號(hào)表示可以為null ,也可以解決上述問題,如圖所示:

3.4 解析

config.setClassForTemplateLoading(obj.getClass(), "/templates"); 中的第一個(gè)參數(shù) obj.getClass() 是用于指定模板加載的基準(zhǔn)類。具體作用如下:

確定類路徑:FreeMarker 根據(jù)傳入的類來確定模板文件所在的類路徑。這里使用 obj.getClass() 表示以 obj 所屬類的類路徑為基準(zhǔn)。加載模板資源:FreeMarker 會(huì)從該類所在的類路徑下查找并加載模板文件,路徑為 /templates。

簡(jiǎn)而言之,第一個(gè)參數(shù)決定了 FreeMarker 在哪里查找模板文件。

此處ftl文件和OpinionCollectController是在同一個(gè)工程目錄下,同一個(gè)target目錄下,所以可以直接傳OpinionCollectController的對(duì)象本身,如果ftl文件和FreemarkerUtil工具類在同一個(gè)工程目錄下,我們也可以將config.setClassForTemplateLoading(obj.getClass(), "/templates");改成config.setClassForTemplateLoading(FreemarkerUtil.class, "/templates");

3.5  運(yùn)行代碼

運(yùn)行測(cè)試案例的代碼,可以看到文件正常導(dǎo)出,且變量已經(jīng)被替換

4.使用模板導(dǎo)出word勾選框

實(shí)際業(yè)務(wù)需求需要根據(jù)業(yè)務(wù)勾選上對(duì)應(yīng)的項(xiàng)目類型,同時(shí)導(dǎo)出的文件需要有點(diǎn)擊方框自動(dòng)勾選的功能,我們可以先在word模板里實(shí)現(xiàn)該功能,然后導(dǎo)出成xml格式的文件

打開xml文件,為了方便查看,我們需要安裝XMLTools的插件

安裝完插件之后,我們可以通過notepad查看xml文件

如圖所示,找到方框的位置,將方框替換為變量

添加代碼對(duì)test1和test2進(jìn)行賦值

public class ExportWordDemo {
    public static void main(String[] args) {
        String templateName = "opinionCollect.ftl";
        String fileName = "opinionCollect.docx";
        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("orgName", "測(cè)試機(jī)構(gòu)02141151");
        dataModel.put("data", "2025-02-14");
        //? □
        dataModel.put("test1", "□");
        dataModel.put("test2", "?");
        
        // 執(zhí)行導(dǎo)出
        FreemarkerUtil.exportWord(templateName, fileName, dataModel,this,response);
    }
}

執(zhí)行代碼我們可以看到導(dǎo)出的文件如下圖,同時(shí)方框也可以點(diǎn)擊勾選或取消勾選

5.生成動(dòng)態(tài)列表格

模板如下圖所示,具體業(yè)務(wù)需求如下:

1、一個(gè)任務(wù)編號(hào)下綁定的有多個(gè)直屬單位

2、每個(gè)直屬單位下需要匯總供應(yīng)商管理部門、質(zhì)量部門、技術(shù)部門、檢測(cè)部門、其他相關(guān)意見的審核意見

5.1 list標(biāo)簽

//whyc 是集合(collection)的表達(dá)式,yc是循環(huán)變量的名字,不能是表達(dá)式
<#list whyc as yc>
 需要循環(huán)的部分 變量用${(yc.supplierReasonDesc)!}
</#list>

首先修改ftl文件,找到我們需要循環(huán)的部門,進(jìn)行添加<#list></#list>標(biāo)簽

此處我們要循環(huán)的是companyName及其其對(duì)應(yīng)的征求部門的意見,我們?cè)趂tl文件找到“備注”這一行結(jié)束的位置,${companyName}這一行開始的位置,添加 <#list whyc as yc>

如下圖所示:

注意: list一定要放在要循環(huán)開始的位置,即前一行結(jié)束的位置

然后我們需要找到循環(huán)結(jié)束的位置,本案例是到相關(guān)意見的 ${otherReasonDesc}處結(jié)束,找到這一行結(jié)束的位置,添加 </#list> 標(biāo)簽,如圖所示:

添加完<#list></#list>標(biāo)簽后,我們需要調(diào)整變量名,將要?jiǎng)討B(tài)生成的變量改為 循環(huán)變量.變量

本案例中,以供應(yīng)商管理部門為例,我們需要將 ${supplierOpinion}改為{yc.supplierOpinion}, ${supplierSituationDesc}改為{yc.supplierSituationDesc}, ${supplierReasonDesc}改為{yc.supplierReasonDesc},質(zhì)量部門、技術(shù)部門等的變量相同方式修改,如圖所示:

5.2 相關(guān)代碼實(shí)現(xiàn)

    public void exportWord(@RequestBody OpinionCollectFeedbackQueryVO query, HttpServletResponse response) throws Exception {
        String templateName = "problemSummary.ftl";
        String fileName = "problemSummary.docx";
        Map<String, Object> dataModel = new HashMap<>();
        Map<String, Object> dataModel1 = new HashMap<>();
        Map<String, Object> dataModel2 = new HashMap<>();
        Map<String, Object> dataModel3 = new HashMap<>();
        List<Map<String, Object>> list = new ArrayList<>();
        dataModel.put("aptitudeTaskCode","202502211035");
        dataModel.put("companyName","com20250221");
        dataModel.put("supplierOpinion","供應(yīng)商管理部門審核意見");
        dataModel.put("supplierSituationDesc","供應(yīng)商管理部門情況描述");
 
        dataModel1.put("companyName","com20250221001");
        dataModel1.put("supplierReasonDesc","供應(yīng)商管理部門原因11111");
        dataModel2.put("companyName","com20250221002");
        dataModel2.put("supplierReasonDesc","供應(yīng)商管理部門原因22222");
        dataModel3.put("companyName","com20250221003");
        dataModel3.put("supplierReasonDesc","供應(yīng)商管理部門原因33333");
 
        list.add(dataModel1);
        list.add(dataModel2);
        list.add(dataModel3);
        dataModel.put("whyc",list);
        // 執(zhí)行導(dǎo)出
        FreemarkerUtil.exportWord(templateName, fileName, dataModel,this,response);
    }

測(cè)試導(dǎo)出結(jié)果如下: 

由圖可見,我們實(shí)現(xiàn)了動(dòng)態(tài)生成且數(shù)據(jù)和我們測(cè)試類里填寫的數(shù)據(jù)一致 

5.3 合并單元格

要做到合并單元格,我們首先需要了解一下ftl文件一些屬性的含義,我們先看下圖:

單元格屬性設(shè)置:

<w:tcPr>:定義單元格的屬性。w:tcW:設(shè)置單元格寬度為1992單位(dxa類型)。w:vMerge:設(shè)置單元格為垂直合并的起始單元格(restart)。w:noWrap:設(shè)置單元格內(nèi)容不換行(0表示不啟用)。w:vAlign:設(shè)置單元格內(nèi)容垂直居中對(duì)齊(center)。

<w:vMerge w:val="continue"/>:設(shè)置單元格為垂直合并的延續(xù)部分(continue),表示該單元格與上方單元格合并。 

如果生成的ftl文件里沒有上述屬性,我們可以 在循環(huán)的部門的第一行放 <w:vMerge w:val="restart"/> 從第二行開始,所有要合并的單元格放<w:vMerge w:val="continue"/>

5.3.1 編輯ftl文件:

start和end的位置,一定要放在循環(huán)開始的<w:tcPr>中:

代碼可以這么處理:

    public List<Map<String, Object>> checkList(List<Map<String, Object>> list){
        String start = "<w:vMerge w:val=\"restart\"/>";
        String end = "<w:vMerge w:val=\"continue\"/>";
        list.get(0).put("start",start);
        for (int i = 1; i < list.size(); i++) {
            list.get(i).put("end",end);
        }
        return list;
    }

以上便可實(shí)現(xiàn)合并單元格 

以上就是Java通過freemarker生成Word文檔導(dǎo)出的方式講解的詳細(xì)內(nèi)容,更多關(guān)于Java freemarker生成Word導(dǎo)出的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中new與clone操作對(duì)象的比較方法舉例

    Java中new與clone操作對(duì)象的比較方法舉例

    這篇文章主要給大家介紹了關(guān)于Java中new與clone操作對(duì)象的比較方法,在java中對(duì)象的誕生是我們開發(fā)人員new出來的,對(duì)象的使用也是我們開發(fā)人員進(jìn)行操作的,需要的朋友可以參考下
    2024-07-07
  • SpringBoot2使用JTA組件實(shí)現(xiàn)基于JdbcTemplate多數(shù)據(jù)源事務(wù)管理(親測(cè)好用)

    SpringBoot2使用JTA組件實(shí)現(xiàn)基于JdbcTemplate多數(shù)據(jù)源事務(wù)管理(親測(cè)好用)

    這篇文章主要介紹了SpringBoot2使用JTA組件實(shí)現(xiàn)基于JdbcTemplate多數(shù)據(jù)源事務(wù)管理(親測(cè)好用),在Spring?Boot?2.x中,整合了這兩個(gè)JTA的實(shí)現(xiàn)分別是Atomikos和Bitronix,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • springboot如何初始化執(zhí)行sql語句

    springboot如何初始化執(zhí)行sql語句

    這篇文章主要介紹了springboot初始化執(zhí)行sql語句的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • springboot獲取properties屬性值的多種方式總結(jié)

    springboot獲取properties屬性值的多種方式總結(jié)

    這篇文章主要介紹了springboot獲取properties屬性值的多種方式總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Nacos作為配置中心注冊(cè)監(jiān)聽器方法

    Nacos作為配置中心注冊(cè)監(jiān)聽器方法

    本文主要討論Nacos作為配置中心時(shí),其中配置內(nèi)容發(fā)生更改時(shí),我們的應(yīng)用程序能夠做的事。一般使用監(jiān)聽器來實(shí)現(xiàn)這步操作,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-02-02
  • java Runtime如何執(zhí)行多條命令

    java Runtime如何執(zhí)行多條命令

    這篇文章主要介紹了java Runtime如何執(zhí)行多條命令,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • 五種Java多線程同步的方法

    五種Java多線程同步的方法

    這篇文章主要為大家詳細(xì)介紹了五種Java多線程同步的方法,需要的朋友可以參考下
    2015-09-09
  • Java初學(xué)者常問的問題(推薦)

    Java初學(xué)者常問的問題(推薦)

    本文介紹一些Java初學(xué)者常問的問題,很多朋友對(duì)可以用%除以一個(gè)小數(shù)嗎? a += b 和 a = a + b 的效果有區(qū)別嗎? 聲明一個(gè)數(shù)組為什么需要花費(fèi)大量時(shí)間? 為什么Java庫不用隨機(jī)pivot方式的快速排序?等等一系列問題有疑惑,下面就通過本文給大家詳細(xì)介紹下
    2017-03-03
  • Spring Cloud OAuth2中/oauth/token的返回內(nèi)容格式

    Spring Cloud OAuth2中/oauth/token的返回內(nèi)容格式

    Spring Cloud OAuth2 生成access token的請(qǐng)求/oauth/token的返回內(nèi)容就需要自定義,本文就詳細(xì)介紹一下,感興趣的可以了解一下
    2021-07-07
  • springboot+vue項(xiàng)目從第一行代碼到上線部署全流程

    springboot+vue項(xiàng)目從第一行代碼到上線部署全流程

    本文詳細(xì)介紹了如何從零開始搭建一個(gè)基于Spring Boot和Vue.js的前后端分離項(xiàng)目,并涵蓋項(xiàng)目需求分析、技術(shù)選型、項(xiàng)目結(jié)構(gòu)設(shè)計(jì)、前后端交互、部署上線等全過程,感興趣的朋友跟隨小編一起看看吧
    2024-11-11

最新評(píng)論