SpringBoot整合Freemarker导出word文档表格

1、pom.xml

在pom.xml文件中添加freemarker的依赖包
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-freemarker</artifactId>
	</dependency>

2、制作.ftl模板

2.1 创建word模板

创建word文档,制定表格。其中需要根据实际替换的元素用${...}书写。
主要包含几种情况:
1)正常需要替换的元素
2)“判断a”、“判断b”演示需要判断是否显示整行
3)“循环合并行”演示没有内容右边为空,有多条数据右边分行显示,左边自动合并。

在这里插入图片描述

2.2 另存为xml格式,进行简单处理

将word文档另存为xml格式。

1)首先找到之前写的替换元素,确保${…}和单词是连在一起的,如果不在一起,就把中间的删掉,处理到一起。
在这里插入图片描述
2)我习惯把xml文件按行给他格式化好
<w:tbl></w:tbl> 表示表格
<w:tr></w:tr> 表示行
<w:tc></w:tc> 表示列
在这里插入图片描述

2.3 创建实体类

public class DemoWordDetail {

    private String title;
    private String type;
    private String time;
    private String aaa;
    private int isShowA=1;//控制行显示
    private String bbb;
    private int isShowB=1;//控制行显示
    private List<CyclicModel> cyclics;//合并数据

}

public class CyclicModel {

    private String cyclic;
    /**
     * 合并标志;第一行"<w:vMerge w:val='restart'/>",后边行"<w:vMerge/>"
     */
    private String merge;


}

2.4处理xml文件

1)对于正常需要替换的元素可以不用做任何操作
2)“判断a”、“判断b”演示需要判断是否显示整行,定义字段isShowA、isShowB来控制

在行<w:tr></w:tr>的外层添加<#if></#if>标签,如果isShowA==1则显示这一行,如果为其他值则不显示。

在这里插入图片描述
3)“循环合并行”处理

采用<#if>显示处理数据<#else>显示空白,把${...}删掉</#if>
加	${cyclics.merge}处理第一列合并问题;
将之前的${cyclic}替换成${cyclics.cyclic}

在这里插入图片描述

2.5修改后缀为ftl

3、导出word方法

3.1将demo.ftl放入resources/template

在这里插入图片描述

3.2通用的word导出方法

@Service
public class WordService {

    public void exportWord(HttpServletRequest request, HttpServletResponse response, String fileName , String templeteName, Object dataModel){

        Configuration configuration=new Configuration();
        configuration.setDefaultEncoding("utf-8");
        configuration.setEncoding(Locale.getDefault(),"utf-8");

        try {

            configuration.setClassicCompatible(true);//处理dataModel中如果为null的情况
	            
       		//既能保证本地运行找得到模板文件,又能保证jar包运行能找到得到模板文件
            configuration.setClassForTemplateLoading(this.getClass(),"/template");
            configuration.setTemplateLoader(new ClassTemplateLoader(this.getClass(),"/template"));
            //            configuration.setDirectoryForTemplateLoading(new File(CommonUtil.getTempletePath()+"/template/"));
            Template t=configuration.getTemplate(templeteName,"utf-8");

            response.setContentType("application/msword; charset=UTF-8");// application/x-download
            response.setHeader("Content-Disposition", "attachment; "
                    + encodeFileName(request, fileName+".doc"));

            OutputStream outputStream = response.getOutputStream();
            Writer out=new OutputStreamWriter(outputStream);
            t.process(dataModel, out);
            outputStream.close();
            out.close();

        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }

    }
    
	public static String encodeFileName(HttpServletRequest request, String fileName)
            throws UnsupportedEncodingException
    {

        String new_filename = URLEncoder.encode(fileName, "UTF8").replaceAll("\\+", "%20");

        String agent = request.getHeader("USER-AGENT").toLowerCase();
        if (null != agent && -1 != agent.indexOf("msie"))
        {
            /**
             * IE浏览器,只能采用URLEncoder编码
             */
            return "filename=\"" + new_filename +"\"";
        }else if (null != agent && -1 != agent.indexOf("applewebkit")){
            /**
             * Chrome浏览器,只能采用ISO编码的中文输出
             */
            return "filename=\"" + new String(fileName.getBytes("UTF-8"),"ISO8859-1") +"\"";
        } else if (null != agent && -1 != agent.indexOf("opera")){
            /**
             * Opera浏览器只可以使用filename*的中文输出
             * RFC2231规定的标准
             */
            return "filename*=" + new_filename ;
        }else if (null != agent && -1 != agent.indexOf("safari")){
            /**
             * Safani浏览器,只能采用iso编码的中文输出
             */
            return "filename=\"" + new String(fileName.getBytes("UTF-8"),"ISO8859-1") +"\"";
        }else if (null != agent && -1 != agent.indexOf("firefox"))
        {
            /**
             * Firfox浏览器,可以使用filename*的中文输出
             * RFC2231规定的标准
             */
            return "filename*=" + new_filename ;
        } else
        {
            return "filename=\"" + new_filename +"\"";
        }
    }

}

3.3执行导出方法


        DemoWordDetail demoWordDetail=new DemoWordDetail();
        //一系列处理
        wordService.exportWord(request, response, "title", "demo.ftl", demoWordDetail);
        

3.4测试效果

判断a、判断b均显示,循环合并行多条数据

判断a这行不显示,循环合并行无数据

4、遇到的坑

1)导出对象字段有为null时,报错,加上“configuration.setClassicCompatible(true);”
2)idea运行能正常导出,jar运行不能找到模板,代码中给出了解决
3)cmd运行jar,能正常导出word,但是打开错误。
	经过测试发现导出word文档乱码,发现是是cmd默认编码问题,在cmd执行时加上:
	start javaw -Dfile.encoding=utf-8 -jar xxx.jar
SpringBoot_Freemarker生成Word_多个表格+两层嵌套循环; 步骤说明: 1.用Microsoft Office Word打开word原件;将文档中需要动态生成的内容,替换为属性名 ${name} 2.另存为,选择保存类型Word 2003 XML 文档(*.xml) 3.用Firstobject free XML editor打开文件,选择Tools下的Indent【或者按快捷键F8】式化文件内容。左边是文档结构,右边是文档内容; 4. 文档生成后有时需要手动修改,查找第一步中设置的属性名,可能会产生类似${n.....ame}类似的样子,我们将将名字中间的标签删掉,恢复为${name} 5. word模板中有表格,需要循环的位置, 用 标签将第二对 标签(即除表头的w:tr标签后的一对)包围起来 同时表格内的属性例如${name},在这里需要修改为${user.name} (userList是集合在dataMap中的key, user是集合中的每个元素, 类似), 如图: PLUS:若表格之外还有嵌套的循环,也需要用,注意这里的标签不要和某对其他标签交叉,不可以出现这种 6. 标识替换完之后,另存为.ftl后缀文件即可。 代码里是相对有一丢丢复杂的,两层嵌套循环; 总(dataMap) deptName 部门名 list(Table)表的集合 table1(map) table-名字 ${map.table} tableName-中文名 ${map.tableName} columnCount-字段数 ${map.columnCount} recordCount-记录数 ${map.recordCount} listA-List--表格1 map.listA column Model属性——字段名 ${model.column} columnName Model属性——字段中文名 ${model.column} rate Model属性——字段占比 ${model.rate} nullValueCount Model属性——字段空值数 ${model.nullValueCount} listB-List--表格2 map.listB …… listC-List--表格3 map.listC …… table2 table-名字 ${map.table} tableName-中文名 ${map.tableName} columnCount-字段数 ${map.columnCount} recordCount-记录数 ${map.recordCount} listA-List--表格1 map.listA column Model属性——字段名 ${model.column} columnName Model属性——字段中文名 ${model.column} rate Model属性——字段占比 ${model.rate} nullValueCount Model属性——字段空值数 ${model.nullValueCount} listB-List--表格2 map.listB …… listC-List--表格3 map.listC …… table3 ……
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值