开发者

Java通过freemarker生成Word文档导出的方式详解

开发者 https://www.devze.com 2025-03-26 13:52 出处:网络 作者: Dawn
目录1.制作ftl模板2.pom文件引入freemarke依赖,安装依赖3.Java代码实现3.1 将ftl模板移入项目中3.2  创建Freemarker工具类3.3 测试案例代码3.4 解析3.5  运行代码4.使用模板导出word勾选框5.生成动态
目录
  • 1.制作ftl模板
  • 2.pom文件引入freemarke依赖,安装依赖
  • 3.Java代码实现
    • 3.1 将ftl模板移入项目中
    • 3.2  创建Freemarker工具类
    • 3.3 测试案例代码
    • 3.4 解析
    • 3.5  运行代码
  • 4.使用模板导出word勾选框
    • 5.生成动态列表格
      • 5.1 list标签
      • 5.2 相关代码实现
      • 5.3 合并单元格
        • 5.3.1 编辑ftl文件:

    1.制作ftl模板

    在Word中制作模板,制作完后以XML格式导出

     新建WORD文件,将所需android参数用特殊符号 ${} 配置,如下图:

    注意:freemarker中的占位符是${},例如:这里使用的是【${name}】的形式,那么传递的数据中就需要有一个叫做【name】的字段。

    Java通过freemarker生成Word文档导出的方式详解

    导出时选择.xml格式导出

    Java通过freemarker生成Word文档导出的方式详解

    用编译器(notepad++)打开看一下动态参数是否正确,并修改后缀名为ftl

    导出XML文件之后,打开这个文件,此时你会看到里面都是XML标签,首先格式化一下,这样看起来会舒服些,可以检查一下你的占位符内容是否满足freemarker语法。因为有些时候,我们导出的XML文件中,可能会将【${xxx}】分隔成两行,从而导致占位符失效,所以有时候需要手动修改一下占位符。导出的Word XML文件内容大致如下所示:

    Java通过freemarker生成Word文档导出的方式详解

    2.pom文件引入freemarke依赖,安装依赖

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

    3.java代码实现

    3.1 将ftl模板移入项目中

    Java通过freemarker生成Word文档导出的方式详解

    3.2  创建Freemarker工具类

    引入freemarker依赖之后,就可以使用Freemarker编写一个工具类,专php门用于处理文件的导出和数据渲染。

    import freemarker.template.Configuration;
    import freemarker.template.Template;
     
    import javax.servlet.http.HttpServletResponse;
    import java.io.Bufferedwriter;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.nio.charset.StandardCharsets;
    import java.util.Map;
     
    /**
     * @version 1.0.0
     * @Date: 2025/2/14 15:05
     * @Author SYG
     * @Description: Freemarker 工具类
     */
    public class FreemarkerUtil {
     
        /**
         * 使用 Freemarker 生成 Word 文件并导出
         * @param templateName 模板文件路径名称
         * @param fileName 生成的文件路径以及名称
         * @param dataModel 填充的数据对象
         * @param response HttpServletResponse 对象
         */
        public static void exportWord(String templateName, String fileName, Map<String, Object> dataModel, Object obj, HttpServletResponse response) {
            generateFile(templateName, fileName, dataModel, obj, response);
        }
     
        /**
         * 使用 Freemarker 生成指定文件并导出
         * @param templateName 模板文件路径名称
         * @param www.devze.comfileName 生成的文件路径以及名称
         * @param dataModel 填充的数据对象
         * @param obj 基准类对象
         * @param response HttpServletResponse 对象
         */
        private static void generateFile(String templateName, String fileName, Map<String, Object> dataModel, Object obj, HttpServletResponse response) {
            try {
                // 1、创建配置对象
                Configuration config = new Configuration(Configuration.VERSION_2_3_30);
                config.setDefaultEncoding("utf-8");
                //设置了模板文件的加载类和路径,使得Freemarker可以从指定的类路径下的/templates目录中加载模板文件。
                config.setClassForTemplateLoading(obj.getClass(), "/templates");
                // 2、获取模板文件
                Template template = config.getTemplate(templateName);
                // 3、设置响应头
                response.setContentType("application/msword");
                response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
                // 4、创建写入器
                Writer writer = new BufferedWriter(new OutputStreamWriter(response.getOutputStream(), StandardCharsets.UTF_8));
                // 5、渲染模板文件
                template.process(dataModel, writer);
                // 6、关闭写入器
                writer.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
     
    }

    3.3 测试案例代码

     
    public class ExportWordDemo {
        public static void main(String[] args) {
            String templateName = "opinionCollect.ftl";
            String fileName = "opinionCollect.docx";
            Map<String, Object> dataModel = new HashMap<>();
            dataModel.put("orgName", "测试机构02141151");
            dataModel.put("data", "2025-02-14");
            // 执行导出
            FreemarkerUtil.exportWord(templateName, fileName, dataModel,this,response);
        }
    }

    案例所使用的ftl文件共有两个变量data和orgName,若map集合里只给data或orgName一个变量赋值(例如我们只给data赋值        

    dataModel.put("data", "2025-02-14");

    ),则会报错:

    FreeMarker template error:

    The following has evaLuated to null or missing:

    ==> orgName [in template "payment.ftl" at line 3, column 23752]

    为解决当前异常,这里有俩种方案:

    • 给orgName赋一个空值,即dataModel.put("orgName", ""); 
    • (推荐)修改模板,根据数据结构字段,将需要填充数据的地方,修改为EL表达式。${()!} 加括号!号表示可以为null ,也可以解决上述问题,如图所示:

    Java通过freemarker生成Word文档导出的方式详解

    3.4 解析

    Java通过freemarker生成Word文档导出的方式详解

    Java通过freemarker生成Word文档导出的方式详解

    config.setClassForTemplateLoading(obj.getClass(), "/templates"); 中的第一个参数 obj.getClass() 是用于指定模板加载的基准类。具体作用如下:

    确定类路径:FreeMarker 根据传入的类来确定模板文件所在的类路径。这里使用 obj.getClass() 表示以 obj 所属类的类路径为基准。加载模板资源:FreeMarker 会从该类所在的类路径下查找并加载模板文件,路径为 /templates。

    简而言之,第一个参数决定了 FreeMarker 在哪里查找模板文件。

    此处ftl文件和OpinionCollectwww.devze.comController是在同一个工程目录下,同一个target目录下,所以可以直接传OpinionCollectController的对象本身,如果ftl文件和FreemarkerUtil工具类在同一个工程目录下,我们也可以将config.setClassForTemplateLoading(obj.getClass(), "/templates");改成config.setClassForTemplateLoading(FreemarkerUtil.class, "/templates");

    3.5  运行代码

    运行测试案例的代码,可以看到文件正常导出,且变量已经被替换

    Java通过freemarker生成Word文档导出的方式详解

    4.使用模板导出word勾选框

    实际业务需求需要根据业务勾选上对应的项目类型,同时导出的文件需要有点击方框自动勾选的功能,我们可以先在word模板里实现该功能,然后导出成xml格式的文件

    Java通过freemarker生成Word文档导出的方式详解

    打开xml文件,为了方便查看,我们需要安装XMLTools的插件

    安装完插件之后,我们可以通过notepad查看xml文件

    Java通过freemarker生成Word文档导出的方式详解

    如图所示,找到方框的位置,将方框替换为变量

    Java通过freemarker生成Word文档导出的方式详解

    添加代码对test1和test2进行赋值

    public class ExportWordDemo {
        public static void main(String[] args) {
            String templateName = "opinionCollect.ftl";
            String fileName = "opinionCollect.docx";
            Map<String, Object> dataModel = new HashMap<>();
            dataModel.put("orgName", "测试机构02141151");
            dataModel.put("data", "2025-02-14");
            //☑ □
            dataModel.put("test1", "□");
            dataModel.put("test2", "☑");
            
            // 执行导出
            FreemarkerUtil.exportWord(templateName, fileName, dataModel,this,response);
        }
    }

    执行代码我们可以看到导出的文件如下图,同时方框也可以点击勾选或取消勾选

    Java通过freemarker生成Word文档导出的方式详解

    5.生成动态列表格

    模板如下图所示,具体业务需求如下:

    1、一个任务编号下绑定的有多个直属单位

    2、每个直属单位下需要汇总供应商管理部门、质量部门、技术部门、检测部门、其他相关意见的审核意见

    Java通过freemarker生成Word文档导出的方式详解

    5.1 list标签

    //whyc 是集合(collection)的表达式,yc是循环变量的名字,不能是表达式
    <#list whyc as yc>
     需要循环的部分 变量用${(yc.supplierReasonDesc)!}
    </#list>

    首先修改ftl文件,找到我们需要循环的部门,进行添加<#list></#list>标签

    此处我们要循环的是companyName及其其对应的征求部门的意见,我们在ftl文件找到“备注”这一行结束的位置,${companyName}这一行开始的位置,添加 <#list whyc as yc>

    如下图所示:

    Java通过freemarker生成Word文档导出的方式详解

    注意: list一定要放在要循环开始的位置,即前一行结束的位置

    然后我们需要找到循环结束的位置,本案例是到相关意见的 ${otherReasonDesc}处结束,找到这一行结束的位置,添加 </#list> 标签,如图所示:

    Java通过freemarker生成Word文档导出的方式详解

    添加完<#list></#list>标签后,我们需要调整变量名,将要动态生成的变量改为 循环变量.变量

    本案例中,以供应商管理部门为例,我们需要将 ${supplierOpinion}改为{yc.supplierOpinion}, ${supplierSituationDesc}改为{yc.supplierSituationDesc}, ${supplierReasonDesc}改为{yc.supplierReasonDesc},质量部门、技术部门等的变量相同方式修改,如图所示:

    Java通过freemarker生成Word文档导出的方式详解

    5.2 相关代码实现

        public void exportWord(@RequestBody OpinionCollectFeedbackQueryVO query, HttpServletResponse response) throws Exception {
            String templateName = "problemSummary.ftl";
            String fileName = "problemSummary.docx";
            Map<String, Object> dataModel = new HashMap<>();
            Map<String, Object> dataModel1 = new HashMap<>();
            Map<String, Object> dataModel2 = new HashMap<>();
            Map<String, Object> dataModel3 = new HashMap<>();
            List<Map<String, Object>> list = new ArrayList<>();
            dataModel.put("aptitudeTaskCode","202502211035");
            dataModel.put("companyName","com20250221");
            dataModel.put("supplierOpinion","供应商管理部门审核意见");
            dataModel.put("supplierSituationDesc","供应商管理部门情况描述");
     
            dataModel1.put("companyName","com20250221001");
            dataModel1.put("supplierReasonDesc","供应商管理部门原因11111");
            dataModel2.put("companyName","com20250221002");
            dataModel2.put("supplierReasonDesc","供应商管理部门原因22222");
            dataModel3.put("companyName","com20250221003");
            dataModel3.put("supplierReasonDesc","供应商管理部门原因33333");
     
            list.add(dataModel1);
            list.add(dataModel2);
            list.add(dataModel3);
            dataModel.put("whyc",list);
            // 执行导出
            FreemarkerUtil.exportWord(templateName, fileName, dataModel,this,response);
        }

    测试导出结果如下: 

    Java通过freemarker生成Word文档导出的方式详解

    由图可见,我们实现了动态生成且数据和我们测试类里填写的数据一致 

    5.3 合并单元格

    要做到合并单元格,我们首先需要了解一下ftl文件一些属性的含义,我们先看下图:

    Java通过freemarker生成Word文档导出的方式详解

    单元格属性设置:

    <w:tcPr>:定义单元格的属性。w:tcW:设置单元格宽度为1992单位(dxa类型)。w:vMerge:设置单元格为垂直合并的起始单元格(restart)。w:noWrap:设置单元格内容不换行(0表示不启用)。wjavascript:vAlign:设置单元格内容垂直居中对齐(center)。

    Java通过freemarker生成Word文档导出的方式详解

    <w:vMerge w:val="continue"/>:设置单元格为垂直合并的延续部分(continue),表示该单元格与上方单元格合并。 

    如果生成的ftl文件里没有上述属性,我们可以 在循环的部门的第一行放 <w:vMerge w:val="restart"/> 从第二行开始,所有要合并的单元格放<w:vMerge w:val="continue"/>

    5.3.1 编辑ftl文件:

    start和end的位置,一定要放在循环开始的<w:tcPr>中:

    Java通过freemarker生成Word文档导出的方式详解

    Java通过freemarker生成Word文档导出的方式详解

    代码可以这么处理:

        public List<Map<String, Object>> checkList(List<Map<String, Object>> list){
            String start = "<w:vMerge w:val=\"restart\"/>";
            String end = "<w:vMerge w:val=\"continue\"/>";
            list.get(0).put("start",start);
            for (int i = 1; i < list.size(); i++) {
                list.get(i).put("end",end);
            }
            return list;
        }

    以上便可实现合并单元格 

    以上就是Java通过freemarker生成Word文档导出的方式讲解的详细内容,更多关于Java freemarker生成Word导出的资料请关注编程客栈(www.devze.com)其它相关文章!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号