Springboot中使用freemarker动态生成word文档

本文介绍了如何在SpringBoot项目中结合Freemarker动态生成Word文档,包括模板的准备,SpringBoot的集成配置,以及实际的导出效果展示。

最近因客户需求,需要在项目中动态导出动态表格。步骤如下:

模板准备 

1.新建word文档

注:如果序号从1开始,可以写成${item_index+1},c标签的同样在这适用。

2.另存为.xml文件(注意不是直接改后缀名),格式化代码,注意检查是否存在变量分离问题,如图:

3.注意将循环遍历的list在表格取数据的tr前添加如下标签

4.格式化并修改完后,将.xml的后缀名修改为.ftl(直接修改) 

springboot集成整合

1.pom.xml中添加freemarker依赖

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-freemarker</artifactId>
     <version>2.3.0.RELEASE</version>
 </dependency>

2.配置freemarker

#freemarker
freemarker:
  allow-request-override: false
  cache: true
  check-template-location: false
  charset: UTF-8
  content-type: text/html;charset=utf-8
  expose-request-attributes: false
  expose-session-attributes: false
  expose-spring-macro-helpers: fase
  request-context-attribute:
    suffix: .ftl
    template-loader-path: classpath:/templates/

3.将test.ftl模板拷贝到项目的resource目录下

4.测试页面导出方法 

function exportDoc(){
            var elemIF = document.createElement("iframe");
            elemIF.src = "${ctx}/exportDoc";
            elemIF.style.display = "none";
            document.body.appendChild(elemIF);
        }

5.测试controller

@Controller
public class TestController {

    @PostMapping("uploadFile")
    @ResponseBody
    public void exportDoc(HttpServletRequest request, HttpServletResponse response) throws Exception{
        Configuration configuration = new Configuration();
        configuration.setDefaultEncoding("utf-8");
        configuration.setClassicCompatible(true);
        //既能保证本地运行找得到模板文件,又能保证jar包运行能找到得到模板文件
        configuration.setClassForTemplateLoading(this.getClass(),"/template");
        configuration.setTemplateLoader(new ClassTemplateLoader(this.getClass(),"/template"));
        Template t=configuration.getTemplate("test.ftl","utf-8");

        response.setContentType("application/msword; charset=UTF-8");// application/x-download
        response.setHeader("Content-Disposition", "attachment; "
                + encodeFileName(request, "导出模板.doc"));
        OutputStream outputStream = response.getOutputStream();
        Writer out=new OutputStreamWriter(outputStream);
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("createUser","admin");
        dataMap.put("tableName","tb_user_info");
        dataMap.put("remark","用户信息");
        List<DataItemBean> itemList = new ArrayList<DataItemBean>();
        DataItemBean itemBean = new DataItemBean();
        itemBean.setItemEnName("user_name");
        itemBean.setItemZhName("用户名");
        itemBean.setItemType("varchar");
        itemBean.setItemLength("32");
        itemList.add(itemBean);
        itemBean.setItemEnName("password");
        itemBean.setItemZhName("密码");
        itemBean.setItemType("varchar");
        itemBean.setItemLength("50");
        itemList.add(itemBean);
        dataMap.put("itemList",itemList);
        t.process(dataMap, out);
        outputStream.close();
        out.close();
    }

    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 +"\"";
        }
    }
}

DataItemBean

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * Created  on 2021-12-24.
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DataItemBean implements Serializable {
	private static final long serialVersionUID = 1L;
	/**
	 * 主键
	 */
	private String id;

	/**
	 * 父节点主键
	 */
	private String tableId;

	/**
	 * 数据项中文名称
	 */
	private String itemZhName;

	/**
	 * 数据项英文名称
	 */
	private String itemEnName;

	/**
	 * 数据类型
	 */
	private String itemType;

	/**
	 * 字段长度
	 */
	private String itemLength;

	/**
	 * 备注
	 */
	private String remark;
}

导出效果

 

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、付费专栏及课程。

余额充值