使用Java + Freemarker 導(dǎo)出word文檔

關(guān)于使用java導(dǎo)出word文檔,網(wǎng)上有很多資料,但基本上來說使用freemarker模板導(dǎo)出的教程居多。所以這次基于網(wǎng)上查到的資料和自己的實踐,記錄下自己的實踐過程,以便日后查閱,也希望能幫到一些人。

下面是基本的例子,以實現(xiàn)簡單的word導(dǎo)出:

1. 組織word對應(yīng)ftl模板

要導(dǎo)出的word模板的內(nèi)容,啟動拼音部分為要在代碼種替換的部分。

模板

編輯好word后將文件另存為.xml文件,然后再將.xml文件后綴改為.ftl。打開ftl文件,依次將變量替換為用${}包裹。注意:替換的內(nèi)容需要包裹在<w:t> </w:t>之中。

另外,最好使用全中文作為占位符。因為使用英文的話,轉(zhuǎn)為xml時,word可能會將一個單詞拆分成兩個,比如我使用Title作為占位符,轉(zhuǎn)化為xml后,搜索的時候一直找不到。然后你會發(fā)現(xiàn),其實word將其拆分成T和itle。這種事也不是絕對的(同一個單詞如果有不同的樣式就會保存在不同的<w:r>中),所以只是建議,即便同一個單詞被拆分了,也不用急等到后面就有解決方案。

word文檔的結(jié)構(gòu)

對于List類型的內(nèi)容來說需要進(jìn)行遍歷。對于上面的數(shù)據(jù)結(jié)構(gòu)來說,我們需要對list進(jìn)行遍歷。在這之前,我們首先了解一下word xml的大概結(jié)構(gòu)

<w:wordDocument>
    <w:body>
        <w:p>
            <w:pPr>
            </w:pPr>    
            <w:r>
                <w:rPr>屬性:加粗,傾斜,字體顏色等</w:rPr>
                <w:t> 文本內(nèi)容</w:t>
            </w:r>      
        </w:p>
    </w:body>
</<w:wordDocument>
  • <w:p> 會包裹一段數(shù)據(jù),(段落)

    • <w:pPr> 段落的屬性,可選元素。 段落屬性的一些示例包括對齊方式、邊框、斷字覆蓋、縮進(jìn)、行距、底紋、文本方向和孤行控制
  • <w:r> 它是具有一組共同屬性(如格式設(shè)置)的文本區(qū)域。它可以包含多個<w:t>元素。如果示例文本中只有一個字是粗體,粗體將會分離到一個<w:r>中

    • <w:rPr>用于指定<w:r>屬性。 連續(xù)文本屬性的一些示例包括粗體、邊框、字符樣式、顏色、字體、字號、斜體、字距調(diào)整、禁用拼寫/語法檢查、底紋、小號大寫字母、刪除線、文字方向和下劃線
  • <w:t> 實際的文本內(nèi)容

    下面我們用一個例子來說明,寫了一些內(nèi)容,并配置了顏色


    示例

    另存為xml文件后的部分代碼

    <w:p wsp:rsidR="0084377C" wsp:rsidRPr="002827FA" wsp:rsidRDefault="009C2113">
        <w:pPr>
            <w:rPr>
                <w:color w:val="000000"/>   
            </w:rPr>
        </w:pPr>
        <w:r>
            <w:rPr><w:rFonts w:hint="fareast"/></w:rPr>
            <w:t>哈哈</w:t>
        </w:r>
        <w:r wsp:rsidRPr="009C2113">
            <w:rPr>
                <w:rFonts w:hint="fareast"/>
                <w:color w:val="FF0000"/>   
            </w:rPr>
            <w:t>嗝</w:t>
        </w:r>
        <w:r wsp:rsidRPr="002827FA">
            <w:rPr>
                <w:rFonts w:hint="fareast"/>
                <w:color w:val="000000"/>
            </w:rPr>
            <w:t>哈哈</w:t>
        </w:r>
    </w:p>
    

從上面可以清楚的看到,上面的內(nèi)容在一個段落里包裹。同時在一個段落里可以設(shè)置多個不同的文字樣式,這部分?jǐn)?shù)據(jù)就會存放在 <w:r> 中,樣式數(shù)據(jù)就存放在<w:rPr> 里面。

所以說如果我們需要遍歷,首先要找到需要遍歷的位置在哪里?找好以后就完成了一半的工作。例如上面的小案例,我們需要遍歷學(xué)號和內(nèi)容。 所以首先定位到 “xuehao” 所在的<w:p> 然后查找 “選項”所在的</w:p>。 然后將這么內(nèi)容使用<#list> </#list>包裹就可以了。

    <#list list as stu>
    <w:tr wsp:rsidR="00B362B3" wsp:rsidRPr="00B55103" wsp:rsidTr="00B55103">
        <w:trPr><w:trHeight w:val="563"/></w:trPr>
        <w:tc>
            <w:tcPr><w:tcW w:w="4148" w:type="dxa"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/></w:tcPr>
            <w:p wsp:rsidR="00B362B3" wsp:rsidRPr="00B55103" wsp:rsidRDefault="00B362B3">
                <w:proofErr w:type="spellStart"/>
                <w:r wsp:rsidRPr="00B55103"><w:t>${stu.xuehao}</w:t></w:r>
                <w:proofErr w:type="spellEnd"/>
            </w:p>
        </w:tc>
        <w:tc>
            <w:tcPr><w:tcW w:w="4148" w:type="dxa"/><w:shd w:val="clear" w:color="auto" w:fill="auto"/></w:tcPr>
            <w:p wsp:rsidR="00B362B3" wsp:rsidRPr="00B55103" wsp:rsidRDefault="00B362B3">
                <w:proofErr w:type="spellStart"/>
                <w:r wsp:rsidRPr="00B55103"><w:rPr><w:rFonts w:hint="fareast"/></w:rPr></w:r>
                <w:r wsp:rsidRPr="00B55103"><w:t>${stu.neirong}</w:t></w:r>
                <w:proofErr w:type="spellEnd"/>
            </w:p>
        </w:tc>
    </w:tr>
    </#list>
2. 添加freemarker依賴
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
        <version>2.3.30</version>
</dependency>
3. 測試代碼
package demo;

import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WordTest {

    private Configuration configuration = null;

    public WordTest() {
        configuration = new Configuration();
        configuration.setDefaultEncoding("UTF-8");
    }

    public static void main(String[] args) {
        WordTest test = new WordTest();
        test.createWord();
    }

    public void createWord() {

        Map<String, Object> dataMap = new HashMap<String, Object>();
        getData(dataMap);
        configuration.setClassForTemplateLoading(this.getClass(), "/");//模板文件所在路徑,此處我是存放在resource目錄下
        try {
            Template t = configuration.getTemplate("wordtemplate.ftl"); //獲取模板文件
            File outFile = new File("D:/outFile" + Math.random() * 10000 + ".doc"); //導(dǎo)出文件
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
            t.process(dataMap, out); //將填充數(shù)據(jù)填入模板文件并輸出到目標(biāo)文件
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void getData(Map<String, Object> dataMap) {
        dataMap.put("title", "標(biāo)題");
        dataMap.put("nian", "2020");
        dataMap.put("yue", "09");
        dataMap.put("ri", "08");
        dataMap.put("shenheren", "李小龍");

        List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
        for (int i = 0; i < 10; i++) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("xuehao", i);
            map.put("neirong", "內(nèi)容" + i);
            list.add(map);
        }
        dataMap.put("list", list);
    }
}

4. 文件結(jié)構(gòu)
文件結(jié)構(gòu)
5. 導(dǎo)出文件效果
效果圖
6. 參考文檔

https://blog.csdn.net/yamadeee/article/details/82771035
https://www.cnblogs.com/lcngu/p/5247179.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容