SpringBoot使用freemarker导出word文件方法详解

 更新时间:2022年11月21日 14:43:54   作者:小学生波波  
这篇文章主要介绍了SpringBoot使用freemarker导出word文件方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧

1、前言

在项目中我们有时间需要根据一个word模板文档,批量生成其他的word文档,里面的有些值改变一下而已,那怎么做呢?

2、需求说明

假如说,现在我有个模板文档,内容如下:

现在上面文档里面有如下变量:

  • username:员工姓名
  • idno:身份证号码
  • hireDate:入职日期
  • work:职位
  • endDate:离职日期

现在我需要针对不同的员工一键生成一份离职证明出来,公司章是一张图片,也需要生成进去,下面我们开始测试

3、编码

基础框架代码地址:https://gitee.com/colinWu_java/spring-boot-base.git

我会在此主干基础上开发

3.1、导入依赖

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

3.2、接口编写

TestController新增下面接口:

/**
     * 根据ftl模板模板下载word文档
     */
@GetMapping("/downloadWord")
public void downloadWord(HttpServletResponse response){
    Map<String, Object> map = new HashMap<>();
    map.put("username", "王天霸");//姓名
    map.put("idno", "429004199601521245");//身份证号
    map.put("hireDate", "2021年12月12日");//入职日期
    map.put("work", "Java软件工程师");//职务
    map.put("endDate", "2022年11月25日");//离职日期
    map.put("imageData", Tools.getBase64ByPath("D:\\demo\\test.png"));//盖章Base64数据(不包含头信息)
    try {
        FreemarkerExportWordUtil.exportWord(response, map, "离职证明.doc", "EmploymentSeparationCertificate.ftl");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

D:\demo\test.png这张图片就是下面这张图片:

3.3、工具类

Tools工具类:

package org.wujiangbo.utils;
import org.wujiangbo.domain.user.User;
import sun.misc.BASE64Encoder;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
 * <p>工具类</p>
 */
public class Tools {
    /**
     * 获得指定图片文件的base64编码数据
     * @param filePath 文件路径
     * @return base64编码数据
     */
    public static String getBase64ByPath(String filePath) {
        if(!hasLength(filePath)){
            return "";
        }
        File file = new File(filePath);
        if(!file.exists()) {
            return "";
        }
        InputStream in = null;
        byte[] data = null;
        try {
            in = new FileInputStream(file);
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        }
        try {
            assert in != null;
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(data);
    }
    /**
     * @desc 判断字符串是否有长度
     */
    public static boolean hasLength(String str) {
        return org.springframework.util.StringUtils.hasLength(str);
    }
}

FreemarkerExportWordUtil工具类:

package org.wujiangbo.utils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.Version;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
 * 模板word导出,工具类
 */
public class FreemarkerExportWordUtil {
    private static Configuration configuration = null;
    static {
        configuration = new Configuration(new Version("2.3.0"));
        configuration.setDefaultEncoding("utf-8");
        //获取模板路径    setClassForTemplateLoading 这个方法默认路径是webRoot 路径下
        configuration.setClassForTemplateLoading(FreemarkerExportWordUtil.class, "/templates");
    }
    private FreemarkerExportWordUtil() {
        throw new AssertionError();
    }
    /**
     * 根据 /resources/templates 目录下的ftl模板文件生成文件并写到客户端进行下载
     * @param response HttpServletResponse
     * @param map 数据集合
     * @param fileName 用户下载到的文件名称
     * @param ftlFileName ftl模板文件名称
     * @throws IOException
     */
    public static void exportWord(HttpServletResponse response, Map map, String fileName, String ftlFileName) throws IOException {
        Template freemarkerTemplate = configuration.getTemplate(ftlFileName);
        // 调用工具类的createDoc方法生成Word文档
        File file = createDoc(map, freemarkerTemplate);
        //将word文档写到前端
        download(file.getAbsolutePath(), response, fileName);
    }
    private static File createDoc(Map<?, ?> dataMap, Template template) {
        //临时文件
        String name = "template.doc";
        File f = new File(name);
        Template t = template;
        try {
            // 这个地方不能使用FileWriter因为需要指定编码类型否则生成的Word文档会因为有无法识别的编码而无法打开
            Writer w = new OutputStreamWriter(new FileOutputStream(f), "utf-8");
            t.process(dataMap, w);
            w.close();
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
        return f;
    }
    //下载文件的公共方法
    public static void download(String filePath, HttpServletResponse response, String fileName) {
        try {
            setAttachmentResponseHeader(response, fileName);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        try(
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
                // 输出流
                BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());
        ){
            byte[] buff = new byte[1024];
            int len = 0;
            while ((len = bis.read(buff)) > 0) {
                bos.write(buff, 0, len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 下载文件名重新编码
     * @param response 响应对象
     * @param realFileName 真实文件名
     * @return
     */
    public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException
    {
        String percentEncodedFileName = percentEncode(realFileName);
        StringBuilder contentDispositionValue = new StringBuilder();
        contentDispositionValue.append("attachment; filename=")
                .append(percentEncodedFileName)
                .append(";")
                .append("filename*=")
                .append("utf-8''")
                .append(percentEncodedFileName);
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
        response.setHeader("Content-disposition", contentDispositionValue.toString());
        response.setHeader("download-filename", percentEncodedFileName);
    }
    /**
     * 百分号编码工具方法
     *
     * @param s 需要百分号编码的字符串
     * @return 百分号编码后的字符串
     */
    public static String percentEncode(String s) throws UnsupportedEncodingException
    {
        String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
        return encode.replaceAll("\\+", "%20");
    }
}

3.4、ftl文件

那么如何制作出ftl模板文件呢?很简单,跟着下面步骤做即可

1、首先新建一个demo.doc文档,然后内容像下面这样写好:

2、将该word文档导出成demo.xml文档,如下:

然后:

这样桌面就会多一个demo.xml文件,此时关闭demo.doc文件,打开demo.xml文件进行编辑

3、修改Demo.xml文件

在demo.xml文件中找到刚才所有的变量,然后都用 包 裹 一 下 , 如 : u s e r n a m e 改 成 {}包裹一下,如:username改成 包裹一下,如:username改成{username},idno改成${idno},等等,全部改完

最后搜索pkg:binaryData字符串,用这个包裹着很大一段字符串,那就是文档中公章图片的Base64格式数据,我们将其全部删除,用${imageData}变量代替即可,待会从代码中给这个变量赋值即可,就可以将真正的公章图片替换到文档中了,而且图片大小格式什么的,都会和模板文档中保持一致,所以模板文档的格式先调整后之后再另存为xml文件

修改完成之后,将文件的后缀名由xml改成ftl,然后拷贝到项目resources下的templates目录下,我这里改成:EmploymentSeparationCertificate.ftl了

3.5、测试

打开浏览器,访问接口:http://localhost:8001/downloadWord,就可以下载一个word文档了,打开后内容如下:

OK,到此测试成功了

4、word转pdf

有些场景需要导出pdf,那么就需要将生成的临时word文件转成pdf后再导出了,下面就介绍一下word转成pdf的方法吧

首先需要导入依赖:

<dependency>
    <groupId>com.documents4j</groupId>
    <artifactId>documents4j-local</artifactId>
    <version>1.0.3</version>
</dependency>
<dependency>
    <groupId>com.documents4j</groupId>
    <artifactId>documents4j-transformer-msoffice-word</artifactId>
    <version>1.0.3</version>
</dependency>

工具类:

/**
     * word文档转成PDF文档
     * @param wordPath word文档路径
     * @param pdfPath pdf文档路径
     */
public static void word2pdf(String wordPath, String pdfPath){
    File inputWord = new File(wordPath);
    File outputFile = new File(pdfPath);
    InputStream docxInputStream = null;
    OutputStream outputStream = null;
    IConverter converter = null;
    try  {
        docxInputStream = new FileInputStream(inputWord);
        outputStream = new FileOutputStream(outputFile);
        converter = LocalConverter.builder().build();
        converter.convert(docxInputStream).as(DocumentType.DOCX).to(outputStream).as(DocumentType.PDF).execute();
        outputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
        log.error("word文档转成PDF文档时,发生异常:{}", e.getLocalizedMessage());
    } finally {
        //关闭资源
        try {
            if(docxInputStream != null){
                docxInputStream.close();
            }
            if(outputStream != null){
                outputStream.close();
            }
            if(converter != null){
                converter.shutDown();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

需要注意的是:要使用这个工具类的话,运行这个代码的机器上需要安装WPS或者office软件,否则转换过程中会报错(这可能是一个小瑕疵,我正在寻找其他更加优雅的转换方案,有更好办法的小伙伴,欢迎留言)

我们就调用这个工具类将刚才生成的word文档转成pdf,成功之后内容如下:

内容和word文档一致,测试成功

5、总结

本文主要介绍了根据word模板如何导出word文件,已经将word文档如何转成pdf文件大家赶紧练习起来吧

到此这篇关于SpringBoot使用freemarker导出word文件方法详解的文章就介绍到这了,更多相关SpringBoot导出word内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于javaweb+jsp的游泳馆会员管理系统(附源码)

    基于javaweb+jsp的游泳馆会员管理系统(附源码)

    这篇文章主要介绍了基于javaweb+jsp的游泳馆会员管理系统,开发工具eclipse/idea/myeclipse/sts等均可配置运行,此源代码社和课程设计,大作业及毕业设计项目,需要的朋友可以参考下
    2022-04-04
  • Mybatis结果集映射一对多简单入门教程

    Mybatis结果集映射一对多简单入门教程

    本文给大家介绍Mybatis结果集映射一对多简单入门教程,包括搭建数据库环境的过程,idea搭建maven项目的代码详解,本文通过实例代码给大家介绍的非常详细,需要的朋友参考下吧
    2021-06-06
  • ZooKeeper入门教程二在单机和集群环境下的安装搭建及使用

    ZooKeeper入门教程二在单机和集群环境下的安装搭建及使用

    本文是ZooKeeper入门系列教程,涵盖ZooKeeper的安装使及单机集群环境搭建,通过实例和大量图表,结合实战,帮助学习者理解和运用,有需要的朋友可以借鉴参考下
    2022-01-01
  • Spring集成Struts与Hibernate入门详解

    Spring集成Struts与Hibernate入门详解

    这篇文章主要给大家介绍了关于Spring集成Struts与Hibernate的相关资料,文中介绍的非常详细,对大家具有一定的参考价值,需要的朋友们下面来一起看看吧。
    2017-03-03
  • Java流形式返回前端的实现示例

    Java流形式返回前端的实现示例

    ​ Java后端开发项目时,需要给前端传一些数据,本文主要介绍了Java流形式返回前端的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • java操作mongodb基础(查询 排序 输出list)

    java操作mongodb基础(查询 排序 输出list)

    java操作mongodb基础学习查询,排序,limit,输出为list实例,大家参考使用吧
    2013-12-12
  • MyBatis中的配置文件详解

    MyBatis中的配置文件详解

    在 MyBatis 中,配置文件分为 全局配置文件(核心配置文件)和映射配置文件,本文给大家介绍MyBatis中的配置文件相关知识,感兴趣的朋友一起看看吧
    2023-10-10
  • Java Restful API的拦截详解

    Java Restful API的拦截详解

    这篇文章主要介绍了Java 调用Restful API接口的拦截,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-09-09
  • Java递归来实现汉诺塔游戏,注释详细

    Java递归来实现汉诺塔游戏,注释详细

    这篇文章介绍了Java递归来实现汉诺塔游戏的方法,文中的代码注释介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • java通过注解翻译字典的实现示例

    java通过注解翻译字典的实现示例

    本文主要介绍了java通过注解翻译字典的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04

最新评论