Java項目中利用Freemarker模板引擎導出--生成Word文檔

應邀寫的一篇文章:Java項目中利用Freemarker模板引擎導出--生成Word文檔

在項目中難免和各種數據報表打交道,如導出XX申請表,登記表,推薦表之類。就可以通過現有信息導出Word文檔。基於Java語言來導出Word文檔的方式也有很多種,如JacobApache POIFreemarkerPageOfficejava2word 等等。。。。

在這裏將通過Freemarker這個模板引擎來實現導出 Word,項目不限於Swing,SSH,SSM,Spring Boot 之類的。。。。。。。。。。

Freemarker介紹

首先說一下Freemarker是個什麼東西:

       FreeMarker是一款模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁、電子郵件、配置文件、源代碼等)的通用工具。 它不是面向最終用戶的,而是一個Java類庫,是一款程序員可以嵌入他們所開發產品的組件。
      FreeMarker是免費的,基於Apache許可證2.0版本發佈。其模板編寫爲FreeMarker Template Language(FTL),屬於簡單、專用的語言。需要準備數據在真實編程語言中來顯示,比如數據庫查詢和業務運算, 之後模板顯示已經準備好的數據。在模板中,主要用於如何展現數據, 而在模板之外注意於要展示什麼數據 [1] 。
                                                                                                ------百度百科

總的來說:模板 + 數據模型 = 輸出

看不懂也沒關係,看一個簡單的例子:通常我們在Java Web開發的時候,實現一個JSP頁面的輸出需要編寫一個JSP文件《xxxx.jsp》,實際上這個JSP文件主要包含了兩部分內容:
HTML+CSS+JavaScript構成的頁面框架 + 用來動態顯示數據庫數據的Java代碼(一般來說直接寫Java代碼是非常不美觀且修改複雜的事情,不符合MVC開發模式所以會用JSTL標籤來代替直接寫Java代碼

靜態標籤-模板
動態數據-數據模型
HTML
網頁框架代碼
CSS
JavaScript
後端數據
JSTL標籤填充
完整的JSP動態頁面

在這裏插入圖片描述Freemarker中也有一套類似於JSTL的標籤,其實簡單來說freemarker就是用來代替jsp來做處理,因爲我們如果用JSP的話,是需要WEB容器的,如tomcat,JSP在第一次執行的時候會被編譯轉換成Servlet類,之後的每次修改都要編譯和轉換。而FreeMarker模板技術並不存在編譯和轉換的問題,並且與容器無關。我們在非web項目下也沒必要再引入一個web容器了。

靜態標籤-模板
動態數據-數據模型
空白Word文檔
XML代碼
後端數據
FTL標籤填充
完整的Word文檔

示例用的項目搭建

下面進入正題:
首先創建一個項目,在這裏的項目可以爲Java swing,Javaweb的SSM,Springboot等項目都是沒問題的。我還是直接用SpringBoot項目實現這個功能了。
首先用IDEA創建一個Spring Boot項目
在這裏插入圖片描述用其他IDE的也可以在https://start.spring.io網站上面直接下載一個springboot項目
在這裏插入圖片描述下一步填完信息,這裏我用默認的信息了
在這裏插入圖片描述開始選擇所需要的插件依賴,也就是jar包,都是通過maven管理的,上圖有說明
1.
在這裏插入圖片描述2. 選擇Spring Web,就是SpringMVC
在這裏插入圖片描述3. 選擇用哪個模板引擎,這裏用Freemarker
在這裏插入圖片描述4. 選擇數據庫框架,這裏用mybaits+mysql
在這裏插入圖片描述5. 下一步,選擇項目路徑,點擊完成就可以了
在這裏插入圖片描述Freemarker主要依賴的jar包是

# Springboot 提供的
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
# 也可以選擇其他版本如:
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.30</version>
</dependency>

# 網址:
https://mvnrepository.com/artifact/org.freemarker/freemarker

FTL模板製作

項目搭建好之後,開始製作FTL模板,這次選擇一個XXX推薦表來實現:
如Word文檔:
在這裏插入圖片描述開始製作模板:
首先,我們需要在模板上面添加佔位符,${XXXXX}。這裏全用了拼音簡寫
在這裏插入圖片描述添加好佔位符之後,將該Word文檔另存爲XML格式的文件
在這裏插入圖片描述修改一下文件名稱,保存在這裏插入圖片描述即得到一個XML文件
在這裏插入圖片描述
打開觀察一下,正常
在這裏插入圖片描述接下來,利用文本編輯器打開這個文件,如Notepad++、Sublime Text、EditPlus、UltraEdit什麼都行,我這裏用VSCode。如圖
在這裏插入圖片描述


直接搜索前面添加的佔位符${xxxx},這裏可能會出現一些問題,如符號和花括號分離,這時候就得對它進行修改,恢復原樣
在這裏插入圖片描述修改後
在這裏插入圖片描述等檢查一遍沒問題之後呢,就修改完成了,開始進入下一步修改文件後綴
在這裏插入圖片描述在這裏插入圖片描述

代碼編寫

接下來開始代碼編寫部分,一般來說這裏都是用數據庫的數據作爲導出數據。但由於麻煩就不寫數據庫部分了,在這裏直接用提交表單的數據來進行導出。

  1. 回到項目上,新建一個實體類,TableData.java ,將佔位符標識作爲類屬性,生成get、set方法。。。。。發現院系YX和郵箱YX重複了,修改郵箱爲DZYX

在這裏插入圖片描述2. 新建簡單的測試頁面
在這裏插入圖片描述
3. 編寫導出工具類

首先將實體類封裝爲導出數據所需的map,創建數據模型
在這裏插入圖片描述**注意:**在這裏直接用的是封裝實體類,所以在模板裏面也需要改一下佔位符,在前面加上對象名${DZYX}變爲${tableData.DZYX},用文本編輯器一鍵搜索${替換即可
在這裏插入圖片描述在這裏插入圖片描述

編寫方法,創建freeMarker配置實例,創建數據模型,加載模板,輸出

    /**
     * 導出Word文件
     *
     * @param tableData
     * @param request
     * @param response
     * @throws Exception
     */
    public InputStreamSource exportToWord(TableData tableData, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 糾正編碼
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("utf-8");
        // ** 初始化配置文件**//*
        Configuration configuration = new Configuration();
        // ** 設置編碼 **//*
        configuration.setDefaultEncoding("utf-8");
        // ** ftl文件的路徑**//*
        String fileDirectory = "D:\\"; //將模板文件放到了D盤下
        // 加載模板文件
        configuration.setDirectoryForTemplateLoading(new File(fileDirectory));
        // 加載模板,通過Word轉XML文件轉換過來的
        Template template = configuration.getTemplate("FreemarkerTest.ftl");
        // 準備數據
        Map<String, Object> dataMap = data(tableData);
        // 指定輸出word文件的路徑
        // 調用工具類WordUtils的createDoc方法生成Word文檔
        InputStreamSource file = UtilTest.createDoc(dataMap, template);
        InputStream fin = file.getInputStream();
        ServletOutputStream out;
        response.setContentType("application/msword");
        // 設置瀏覽器以下載的方式處理該文件
        response.setHeader("content-disposition", "attachment;filename=document.doc");
        out = response.getOutputStream();
        // 緩衝區
        byte[] buffer = new byte[512];
        int bytesToRead;
        // 通過循環將讀入的Word文件的內容輸出到瀏覽器中
        while ((bytesToRead = fin.read(buffer)) != -1) {
            out.write(buffer, 0, bytesToRead);
        }
        fin.close();
        if (out != null) {
            out.close();
        }
        return file;
    }

創建word文件

    /**
     * 創建word文件
     *
     * @param dataMap
     * @param template
     * @return
     * @throws TemplateException
     * @throws IOException
     */
    public static InputStreamSource createDoc(Map<String, Object> dataMap, Template template)
            throws TemplateException, IOException {
        //生成隨機的合同名稱
        StringWriter out1 = new StringWriter();
        Writer out = new BufferedWriter(out1, 10240);
        //將數據輸出到模板
        template.process(dataMap, out);
        out.close();
        out1.close();
        return new ByteArrayResource(out1.toString().getBytes(StandardCharsets.UTF_8));
    }
  1. 編寫控制層
    在這裏插入圖片描述
    運行項目,跳到測試頁面,填寫相應信息
    在這裏插入圖片描述
    提交表單,會彈出下載框
    在這裏插入圖片描述
    將文件下載到本地,打開,可以看到已經填充完成,
    在這裏插入圖片描述至此,基於Java和freemarker實現的Word文件導出功能已經實現了

再回顧一下步驟:

  1. 編輯好格式的Word文檔 1份

  2. 將Word文檔裏需要填充的地方加上佔位符${xxxx}

  3. 將編輯好佔位符的文檔另存爲XML格式Word 2003 XML文檔,並重命名,用英文命名

  4. 利用文本編輯器打開該XML文件檢查,搜索第二步編輯的佔位符,遇到$和 { } 分離的情況則進行修改。檢查完畢後保存退出。

  5. 將檢查完成的XML文件修改後綴名爲 xxx.ftl

  6. 模板編輯完成

  7. 建立Java項目,引入jar包

<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.30</version>
</dependency>
  1. 編寫對應數據的實體類
  2. 編寫測試頁面
  3. 編寫導出的工具類(核心),並修改模板對應的佔位符,有需要的話
  4. 編寫控制層
  5. 測試

代碼

項目代碼–工具類

package com.hh.onlinelearning.util;

import com.hh.onlinelearning.entity.TableData;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.InputStreamSource;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class UtilTest {
    /**
     * 導出Word文件
     *
     * @param tableData
     * @param request
     * @param response
     * @throws Exception
     */
    public InputStreamSource exportToWord(TableData tableData, HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 糾正編碼
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("utf-8");
        // ** 初始化配置文件**//*
        Configuration configuration = new Configuration();
        // ** 設置編碼 **//*
        configuration.setDefaultEncoding("utf-8");
        // ** ftl文件的路徑**//*
        String fileDirectory = "D:\\";
        // 加載模板文件
        configuration.setDirectoryForTemplateLoading(new File(fileDirectory));
        // 加載模板,通過Word轉XML文件轉換過來的
        Template template = configuration.getTemplate("FreemarkerTest.ftl");
        // 準備數據
        Map<String, Object> dataMap = data(tableData);
        // 指定輸出word文件的路徑
        // 調用工具類WordUtils的createDoc方法生成Word文檔
        InputStreamSource file = UtilTest.createDoc(dataMap, template);
        InputStream fin = file.getInputStream();
        ServletOutputStream out;
        response.setContentType("application/msword");
        // 設置瀏覽器以下載的方式處理該文件
        response.setHeader("content-disposition", "attachment;filename=document.doc");
        out = response.getOutputStream();
        // 緩衝區
        byte[] buffer = new byte[512];
        int bytesToRead;
        // 通過循環將讀入的Word文件的內容輸出到瀏覽器中
        while ((bytesToRead = fin.read(buffer)) != -1) {
            out.write(buffer, 0, bytesToRead);
        }
        fin.close();
        if (out != null) {
            out.close();
        }
        return file;
    }

    /**
     * 構造數據,key-value
     *
     * @param tableData
     * @return
     */
    public Map<String, Object> data(TableData tableData) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("tableData", tableData);
        return dataMap;
    }

    /**
     * 創建word文件
     *
     * @param dataMap
     * @param template
     * @return
     * @throws TemplateException
     * @throws IOException
     */
    public static InputStreamSource createDoc(Map<String, Object> dataMap, Template template)
            throws TemplateException, IOException {
        //生成隨機的合同名稱
        StringWriter out1 = new StringWriter();
        Writer out = new BufferedWriter(out1, 10240);
        //將數據輸出到模板
        template.process(dataMap, out);
        out.close();
        out1.close();
        return new ByteArrayResource(out1.toString().getBytes(StandardCharsets.UTF_8));
    }
}

控制層代碼:

    /**
     * 提交表單,下載Word文件
     * @param tableData
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @PostMapping("/export")
    public String a(TableData tableData, HttpServletRequest request, HttpServletResponse response) throws Exception {
        UtilTest u = new UtilTest();
        u.exportToWord(tableData,request,response);
        return "";
    }

    /**
     * 跳轉到表單填寫頁
     * @return
     */
    @GetMapping("/")
    public String index() {
        return "/upload";
    }

實體類代碼:

package com.hh.onlinelearning.entity;

/**
 * 對應Word文檔的佔位符,實體類
 */
public class TableData {
    private int id;
    private String XM;
    private String XB;
    private String MZ;
    private String CSNY;
    private String ZZMM;
    private String YX;
    private String ZY;
    private String SYD;
    private String XL;
    private String XW;
    private String DH;
    private String SG;
    private String TZ;
    private String DZYX;
    private String SLZ;
    private String SLY;
    private String LXDZ;
    private String WY;
    private String JSJ;
    private String JYYX;
    private String AH;
    private String JWJD;
    private String SHSJ;
    private String RZQK;
    private String HJQK;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getXM() {
        return XM;
    }

    public void setXM(String XM) {
        this.XM = XM;
    }

    public String getXB() {
        return XB;
    }

    public void setXB(String XB) {
        this.XB = XB;
    }

    public String getMZ() {
        return MZ;
    }

    public void setMZ(String MZ) {
        this.MZ = MZ;
    }

    public String getCSNY() {
        return CSNY;
    }

    public void setCSNY(String CSNY) {
        this.CSNY = CSNY;
    }

    public String getZZMM() {
        return ZZMM;
    }

    public void setZZMM(String ZZMM) {
        this.ZZMM = ZZMM;
    }

    public String getYX() {
        return YX;
    }

    public void setYX(String YX) {
        this.YX = YX;
    }

    public String getZY() {
        return ZY;
    }

    public void setZY(String ZY) {
        this.ZY = ZY;
    }

    public String getSYD() {
        return SYD;
    }

    public void setSYD(String SYD) {
        this.SYD = SYD;
    }

    public String getXL() {
        return XL;
    }

    public void setXL(String XL) {
        this.XL = XL;
    }

    public String getXW() {
        return XW;
    }

    public void setXW(String XW) {
        this.XW = XW;
    }

    public String getDH() {
        return DH;
    }

    public void setDH(String DH) {
        this.DH = DH;
    }

    public String getSG() {
        return SG;
    }

    public void setSG(String SG) {
        this.SG = SG;
    }

    public String getTZ() {
        return TZ;
    }

    public void setTZ(String TZ) {
        this.TZ = TZ;
    }

    public String getDZYX() {
        return DZYX;
    }

    public void setDZYX(String DZYX) {
        this.DZYX = DZYX;
    }

    public String getSLZ() {
        return SLZ;
    }

    public void setSLZ(String SLZ) {
        this.SLZ = SLZ;
    }

    public String getSLY() {
        return SLY;
    }

    public void setSLY(String SLY) {
        this.SLY = SLY;
    }

    public String getLXDZ() {
        return LXDZ;
    }

    public void setLXDZ(String LXDZ) {
        this.LXDZ = LXDZ;
    }

    public String getWY() {
        return WY;
    }

    public void setWY(String WY) {
        this.WY = WY;
    }

    public String getJSJ() {
        return JSJ;
    }

    public void setJSJ(String JSJ) {
        this.JSJ = JSJ;
    }

    public String getJYYX() {
        return JYYX;
    }

    public void setJYYX(String JYYX) {
        this.JYYX = JYYX;
    }

    public String getAH() {
        return AH;
    }

    public void setAH(String AH) {
        this.AH = AH;
    }

    public String getJWJD() {
        return JWJD;
    }

    public void setJWJD(String JWJD) {
        this.JWJD = JWJD;
    }

    public String getSHSJ() {
        return SHSJ;
    }

    public void setSHSJ(String SHSJ) {
        this.SHSJ = SHSJ;
    }

    public String getRZQK() {
        return RZQK;
    }

    public void setRZQK(String RZQK) {
        this.RZQK = RZQK;
    }

    public String getHJQK() {
        return HJQK;
    }

    public void setHJQK(String HJQK) {
        this.HJQK = HJQK;
    }
}

HTML測試頁:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>測試</title>
</head>
<body>
<h2>測試</h2>
<!--表單-start-->
<form  id="form" method="post" action="/export">
    <input name="XM" type="text" placeholder="姓名" >
    <input name="XB" type="text" placeholder="性別">
    <input name="MZ" type="text" placeholder="民族">
    <input name="CSNY" type="text" placeholder="出生年月">
    <input name="ZZMM" type="text" placeholder="政治面貌">
    <input name="YX" type="text" placeholder="院系">
    <input name="ZY" type="text" placeholder="專業">
    <input name="SYD" type="text" placeholder="生源地">
    <input name="XL" type="text" placeholder="學歷">
    <input name="XW" type="text" placeholder="學位">
    <input name="DH" type="text" placeholder="電  話">
    <input name="SG" type="text" placeholder="身高">
    <input name="TZ" type="text" placeholder="體重">
    <input name="DZYX" type="text" placeholder="E-mail">
    <input name="SLZ" type="text" placeholder="視力Z">
    <input name="SLY" type="text" placeholder="視力Y">
    <input name="LXDZ" type="text" placeholder="聯繫地址">
    <input name="WY" type="text" placeholder="外語程度">
    <input name="JSJ" type="text" placeholder="計算機">
    <input name="JYYX" type="text" placeholder="就業意向">
    <input name="AH" type="text" placeholder="愛好特長">
    <input name="JWJD" type="text" placeholder="自我鑑定">
    <input name="SHSJ" type="text" placeholder="社會實踐">
    <input name="RZQK" type="text" placeholder="任職情況">
    <input name="HJQK" type="text" placeholder="獲獎情況">
    <input id="upload" type="submit" value="提交">
</form>
<!--表單-end-->
</body>
</html>

freemarker 各個版本的jar包:Maven倉庫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章