Java使用FreeMarker来实现Word自定义导出功能

 更新时间:2025年09月26日 11:08:12   作者:保加利亚的风  
Java FreeMarker是一个用Java语言编写的模板引擎,它可以基于模板来生成文本输出,并且与Web容器无关,在对一些特定导出功能,使用常规Excel无法解决的,通常使用Word来实现导出功能,这篇介绍下如何在Java中使用FreeMarker模板注入方式来实现Word导出功能

前言

在对一些特定导出功能,使用常规Excel无法解决的,通常使用Word来实现导出功能,这篇介绍下如何在Java中使用FreeMarker模板注入方式来实现Word导出功能

导出案例(已作打码处理)

或者:

准备工作

第一步:maven依赖库

		<!-- freemarker (用于Word导出)-->
       	<dependency>
	        <groupId>org.freemarker</groupId>
	        <artifactId>freemarker</artifactId>
	        <version>${freemarker.version}</version>
       	</dependency>

第二步:Word导出工具类

import com.zrxt.common.config.RuoYiConfig;
import com.zrxt.common.core.text.CharsetKit;
import freemarker.cache.ClassTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;

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

/**
 * @ClassName WordUtil
 * @Description 使用Freemarker生成Word文档工具类
 * @Author
 * @Date 2023/12/14
 **/
public class WordUtil {
    /**
     * 使用Freemarker自动生成Word文档(磁盘路径方法)
     *
     * @param dataMap      保存Word文档中所需要的数据
     * @param templatePath 模板文件的绝对路径
     * @param templateFile 模板文件的名称
     * @throws Exception
     */
    public static void CreateWord(HttpServletResponse response, Map<String, Object> dataMap, String templatePath, String templateFile) throws Exception {
        Writer out = null;
        try {
            // 设置FreeMarker的版本
            Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
            // 设置Freemarker的编码格式
            configuration.setDefaultEncoding(CharsetKit.UTF_8);
            // 设置FreeMarker生成Word文档所需要的模板的路径
            configuration.setDirectoryForTemplateLoading(new File(templatePath));
            // 设置FreeMarker生成Word文档所需要的模板名称
            Template t = configuration.getTemplate(templateFile, CharsetKit.UTF_8);
            // 创建一个Word文档的输出流
            out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));
            //FreeMarker使用Word模板和数据生成Word文档
            t.process(dataMap, out);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            assert out != null;
            out.flush();
            out.close();
        }
    }

    /**
     * 使用Freemarker自动生成Word文档
     *
     * @param dataMap      保存Word文档中所需要的数据
     * @param templatePath 模板文件的路径(绝对)
     * @param templateFile 模板文件的名称
     * @throws Exception
     */
    public static void GeneratorWord(HttpServletResponse response, Map<String, Object> dataMap, String templatePath, String templateFile) throws Exception {
        Writer out = null;
        try {
            // 设置FreeMarker的版本
            Configuration configuration = new Configuration(Configuration.VERSION_2_3_28);
            // 设置Freemarker的编码格式
            configuration.setDefaultEncoding(CharsetKit.UTF_8);
            //相对路径加载模板方法
            configuration.setTemplateLoader(new ClassTemplateLoader(WordUtil.class,templatePath));
            // 设置FreeMarker生成Word文档所需要的模板名称
            Template template  = configuration.getTemplate(templateFile, CharsetKit.UTF_8);
            // 创建一个Word文档的输出流
            out = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));
            //FreeMarker使用Word模板和数据生成Word文档
            template .process(dataMap, out);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            assert out != null;
            out.flush();
            out.close();
        }
    }

    /**
     * 下载文件
     *
     * @param path     文件的位置
     * @param fileName 自定义下载文件的名称
     * @param response http响应
     * @param request  http请求
     */
    public static void downloadFile(String path, String fileName, HttpServletResponse response, HttpServletRequest request) {
        try {
            File file = new File(path);
            // 中文乱码解决
            String type = request.getHeader("User-Agent").toLowerCase();
            if (type.indexOf("firefox") > 0 || type.indexOf("chrome") > 0) {
                // 谷歌或火狐
                fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), "iso8859-1");
            } else {
                // IE
                fileName = URLEncoder.encode(fileName, CharsetKit.UTF_8);
            }
            // 设置响应的头部信息
            response.setHeader("content-disposition", "attachment;filename=" + fileName);
            // 设置响应内容的类型
            response.setContentType(getFileContentType(fileName) + "; charset=" + CharsetKit.UTF_8);
            // 设置响应内容的长度
            response.setContentLength((int) file.length());
            // 输出
            outStream(new FileInputStream(file), response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件的内容类型
     */
    private static String getFileContentType(String name) {
        String result = "";
        String fileType = name.toLowerCase();
        if (fileType.endsWith(".png")) {
            result = "image/png";
        } else if (fileType.endsWith(".gif")) {
            result = "image/gif";
        } else if (fileType.endsWith(".jpg") || fileType.endsWith(".jpeg")) {
            result = "image/jpeg";
        } else if (fileType.endsWith(".svg")) {
            result = "image/svg+xml";
        } else if (fileType.endsWith(".doc")) {
            result = "application/msword";
        } else if (fileType.endsWith(".xls")) {
            result = "application/x-excel";
        } else if (fileType.endsWith(".zip")) {
            result = "application/zip";
        } else if (fileType.endsWith(".pdf")) {
            result = "application/pdf";
        } else {
            result = "application/octet-stream";
        }
        return result;
    }


    /**
     * 基础字节数组输出
     */
    private static void outStream(InputStream is, OutputStream os) {
        try {
            byte[] buffer = new byte[10240];
            int length = -1;
            while ((length = is.read(buffer)) != -1) {
                os.write(buffer, 0, length);
                os.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                os.close();
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 检查存储生成文件的路径是否存在,如果不存在则新建路径.
     *
     * @param directory the directory name, like '\dir-name'
     */
    public static void CheckDownloadPath(String directory) {
        File path = new File(RuoYiConfig.getDownloadPath() + directory);
        if (!path.exists()) {
            path.mkdirs();
        }
    }
}

模板准备

第一步:首先要编写Word模板,可以参考文章开头的示例图片。
第二步:Word模板编写好后,点击另存为,然后选择Word 2003 XML文档

第三步:保存好后的文件打开,Ctrl+A复制文档所有内容,然后在线搜索XML格式化在线工具,我这边提供一个现成的XML格式化在线工具,将内容全部粘贴进去后点击格式化按钮。

创建ftl文件

上面所有工作准备好后,就可以在resource目录下创建一个.ftl文件,然后将格式化后的代码复制到文件中即可。

测试导出是否完整

Controller层编写测试代码,判断是否可以正常导出

    /**
     * 导出(word)关键过程控制详细信息详细信息(xml版本)
     */
    @PostMapping(value = "/exportWord")
    public void getInfo(MakeCriticalProcessControl param, HttpServletResponse response) {
        try {
            MakeCriticalProcessControl obj=
                    makeCriticalProcessControlService.selectById(param.getId());
          
            Map<String, Object> context = new HashMap<>();
            context.put("obj", obj);
            WordUtil.GeneratorWord(response, context, "/wordDocumentFtl/make/", "MakeCriticalProcessControl.ftl");
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("导出Word文件失败,请稍后重试!");
        }
    }

WordUtil.GeneratorWord()方法
第二个参数context为数据库的数据,要往word中空白单元格所填充的。
第三个参数为templatePath – 模板文件的路径(相对路径)。
第四个参数为具体的模板的文件名称(带后缀)

注:测试后需检查格式是否混乱,是否有缺少单元格等情况,如果有则检查原Word模板并修复然后重新走一遍流程即可

如何将数据库数据填充到Word中

ftl文件中的内容是一行一行的顺序,也就是从第一行的第一个单元格开始,然后是第一行第二个单元格,第三个单元格…
然后第一行如果结束了,则是第二行第一个单元格、第二个单元格、第三个…

以此类推,所以优先找到对应标题的对应数据单元格在ftl文件的哪个位置。

如果是单个属性或对象类型的,可以在controller中使用Map<String, Object> context = new HashMap<>();将数据put,然后key作为键,在ftl文件中可以把属性获取出来。例如下图

如果涉及到遍历,将一个list结果导出到word,请看以下案例

如果是遍历导出的话,则在word模板设置时,只需要设置一行表头,下方对应一行空的单元格即可,我们要循环空的单元格,然后将数据挨个写入。

只需要在标题下写一行即可

在controller中依旧是查询出对应list,然后put到map中

1:判断list是否为空
2:循环遍历list
3:一般<w:tr>表示为行,所以这里的意思就是,list有多少数据,则就生成多少行
如何要获取值的话就可以${item.属性名!}

如果涉及到If语句或者是需要将数字转为对应的状态

以上就是Java使用FreeMarker来实现Word自定义导出功能的详细内容,更多关于Java FreeMarker Word自定义导出的资料请关注脚本之家其它相关文章!

相关文章

  • Spring Data JPA中的动态查询实例

    Spring Data JPA中的动态查询实例

    本篇文章主要介绍了详解Spring Data JPA中的动态查询。小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • SpringBoot 整合mapstruct的实现步骤

    SpringBoot 整合mapstruct的实现步骤

    这篇文章主要介绍了SpringBoot整合mapstruct,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • 教您如何3分钟快速搞定EasyExcel导入与导出功能

    教您如何3分钟快速搞定EasyExcel导入与导出功能

    对于EasyExcel库,我们可以使用它来实现数据的导入和导出,下面这篇文章主要给大家介绍了关于如何3分钟快速搞定EasyExcel导入与导出功能的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • SpringBoot基本web开发demo过程解析

    SpringBoot基本web开发demo过程解析

    这篇文章主要介绍了SpringBoot基本web开发demo过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-11-11
  • java监听器的实现和原理详解

    java监听器的实现和原理详解

    这篇文章主要给大家介绍了关于java监听器实现和原理的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • Spring Boot 如何使用Liquibase 进行数据库迁移(操作方法)

    Spring Boot 如何使用Liquibase 进行数据库迁移(操作方法)

    在Spring Boot应用程序中使用Liquibase进行数据库迁移是一种强大的方式来管理数据库模式的变化,本文重点讲解如何在Spring Boot应用程序中使用Liquibase进行数据库迁移,从而更好地管理数据库模式的变化,感兴趣的朋友跟随小编一起看看吧
    2023-09-09
  • Java基础入门语法--String类

    Java基础入门语法--String类

    字符串广泛应用在Java编程中,在Java中字符串属于对象,Java 提供了String类来创建和操作字符串,今天给大家介绍Java基础入门语法--String类的相关知识,感兴趣的朋友一起看看吧
    2021-06-06
  • java调用webservice接口,并解析返回参数问题

    java调用webservice接口,并解析返回参数问题

    这篇文章主要介绍了java调用webservice接口,并解析返回参数问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-07-07
  • Java面向对象设计原则之迪米特法则介绍

    Java面向对象设计原则之迪米特法则介绍

    迪米特法则解决类与类之间耦合度问题,如果类A调用了B类的某一个方法,则这两个类就形成了一种紧耦合的方式,当B类这个方法发生变化时,一定会影响A类的执行结果。迪米特法则要求每一个类尽可能少的与其他类发生关系
    2023-02-02
  • Java StringBuilder的用法示例

    Java StringBuilder的用法示例

    这篇文章主要给大家介绍了关于Java StringBuilder用法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01

最新评论