Java项目中利用Freemarker模板引擎导出--生成Word文档

倖福魔咒の 提交于 2020-08-05 00:09:44

应邀写的一篇文章: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仓库

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!