Bootstrap

【freemarker】动态生成Excel表单

1.需求

按照模板样式导出数据报表,其中每个要导出的数据对象名称不是固定的,需要根据返回数据自动添加,而且每个对象下的选项内容也不是唯一的,需要自行设置大小
需求模板文件

2.操作步骤

  • 建立一个类似的Excel模板新建模板文件
  • 将模板转化成可以编辑状态
    将Excel模板另存为 .xml 格式
    模板文件另存为xml文件
    根据模板内容进行编辑设置
    查看xml编码
    [注:]推荐一个在线格式化网址:https://tool.oschina.net/codeformat/xml
    格式化xml编码格式化编码文件1
    直接将格式化后的文件后缀名改为 .ftl
    在这里插入图片描述
  • 观察xml文件格式,然后找规律进行动态编辑
    在这里插入图片描述
    我这里是针对 < Worksheet ss:Name=“Sheet1”> < /Worksheet > 表单 1的操作
    ss:ExpandedRowCount=“4” 默认4行,后续根据数据的list长度来确定***
ss:ExpandedRowCount="${list?size+4}"

在这里插入图片描述
算出模板总跨度。对标题进行合并单元格

<Cell ss:StyleID="s60" ss:MergeAcross="${numsAll}">
   <Data ss:Type="String">测试模板</Data>
</Cell>
<#list></#list> 这个是遍历标签,我们这里是对象名称要遍历动态展示,这个标签可以动态渲染表格
()!''   是非空判断,为空展示 ' ' 里面的内容
<#list objectNames as objectNames>
    <Cell ss:StyleID="s61" ss:MergeAcross="${objectNames.length}">
     	<Data ss:Type="String">${(objectNames.objectName)!''}</Data>
   	</Cell>
</#list>
根据模板的代码可以看出,我的是从第三列开始遍历渲染的,所以删掉原来内容,从第三列开始遍历渲染表哥内容
 ss:Index="3" 从第三列开始遍历渲染的
<Row ss:Height="35">
   	<#list questionNames as questionNames>
       <#if questionNames_index == 0>
      		<Cell ss:Index="3" ss:StyleID="s58">
             <Data ss:Type="String">${(questionNames.questionName)!''}</Data>
       		</Cell>
        <#else>
        	<Cell ss:StyleID="s58">
       		 <Data ss:Type="String">${(questionNames.questionName)!''}</Data>
        	</Cell>
      </#if>
    </#list>
</Row>

在这里插入图片描述

渲染答案内容

在这里插入图片描述

第一个<#list>标签:为了渲染多少行
第二个<#list>是答案的内容渲染
<#list list as list>
   <Row>
    <#list list.observeContents as observeContent>
      <Cell>
         <Data ss:Type="String">${(observeContent.answerContent)!''}</Data>
      </Cell>
    </#list>
   </Row>
</#list>

3.代码部分

@ApiOperation("报表统计 -- 导出")
@PostMapping("/getPatrolStandardVoExport")
public void getPatrolStandardVoExport(@RequestBody ReportQueryFieldsVo queryFieldsVo, HttpServletResponse response) throws Exception {
	// 1.获取数据
	Map<String,Object> data = new HashMap<String, Object>();
	data.put("list", list);// 数据长度+答案内容
	data.put("numsAll", 12);// 这个是测试模板需要合并的单元格数量
    data.put("objectNames",objectNames);// 对象标题内容
    data.put("questionNames",questionNames);// 每个对象下的问题内容
    // 模板路径
 	String templateFilePath = Thread.currentThread().getContextClassLoader().getResource("excelTemplate").getPath();
    // 输出路径 MskjConfig.getDownloadPath() 是我当前系统默认配置的一个路径
    String excelPath = MskjConfig.getDownloadPath()+"/excelExport";
    try {
            response.setContentType("application/x-xls");
            response.setCharacterEncoding("UTF-8");
            // 这个地方文件已经生成了,只是前端需要响应流,又加了response
            File file = TemplateParseUtil.parse(templateFilePath, "statistics.ftl", excelPath, "statistics.xls", data,response);
            InputStream inStream = new FileInputStream(file);
            // 设置输出的格式
            response.addHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode("statistics", "UTF-8") + ".xls");
            // 循环取出流中的数据
            byte[] b = new byte[100];
            int len;
            try {
                while ((len = inStream.read(b)) > 0)
                    response.getOutputStream().write(b, 0, len);
                inStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (TemplateException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

TemplateParseUtil 模板工具

import java.io.*;
import java.util.Map;

import cn.hutool.core.util.RandomUtil;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;

/**
 * 模板解析实体类
 */
@Slf4j
public class TemplateParseUtil {
    /**
     * 解析模板生成Excel
     * @param templateDir  模板目录
     * @param templateName 模板名称
     * @param excelPath 生成的Excel文件路径
     * @param data 数据参数
     * @throws IOException
     * @throws TemplateException
     */
    public static File parse(String templateDir, String templateName, String excelPath, String fileName, Map<String,Object> data, HttpServletResponse response) throws Exception {
        //初始化工作
        Configuration cfg = new Configuration();
        //设置默认编码格式为UTF-8
        cfg.setDefaultEncoding("UTF-8");
        //全局数字格式
        cfg.setNumberFormat("0.00");
        //设置模板文件位置
        cfg.setDirectoryForTemplateLoading(new File(templateDir));
        cfg.setObjectWrapper(new DefaultObjectWrapper());
        //加载模板
        Template template = cfg.getTemplate(templateName,"utf-8");
        OutputStreamWriter writer = null;
        File newFile = new File(excelPath);
        if(!newFile.exists()){
            newFile.mkdirs();
        }
        String path = excelPath+File.separator+RandomUtil.randomNumbers(6)+fileName;
        try{
            //填充数据至Excel  文件不存在
            writer = new OutputStreamWriter(new FileOutputStream(path,true),"UTF-8");
            template.process(data, writer);
            writer.flush();
        }finally{
            writer.close();
        }
        InputStream inStream = new FileInputStream(path);
        return  InputStreamToFile(inStream);
    }


    /**
     * 解析模板返回字节数组
     * @param templateDir  模板目录
     * @param templateName 模板名称
     * @param data 数据参数
     * @throws IOException
     * @throws TemplateException
     */
    public static byte[] parse(String templateDir,String templateName,Map<String,Object> data) throws TemplateException, IOException{
        Configuration cfg = new Configuration();
        cfg.setDefaultEncoding("UTF-8");
        cfg.setNumberFormat("0.00");
        cfg.setDirectoryForTemplateLoading(new File(templateDir));
        cfg.setObjectWrapper(new DefaultObjectWrapper());
        Template template = cfg.getTemplate(templateName,"utf-8");
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        Writer out = new OutputStreamWriter(outStream,"UTF-8");
        template.process(data, out);
        return outStream.toByteArray();
    }

    /**
     * 自定义模板字符串解析
     * @param templateStr  模板字符串
     * @param data 数据
     * @return 解析后的字符串
     * @throws IOException
     * @throws TemplateException
     */
    public static String parse(String templateStr, Map<String, Object> data) throws IOException, TemplateException {
        Configuration cfg = new Configuration();
        cfg.setNumberFormat("#.##");
        //设置装载模板
        StringTemplateLoader stringLoader = new StringTemplateLoader();
        stringLoader.putTemplate("myTemplate", templateStr);
        cfg.setTemplateLoader(stringLoader);
        //加载装载的模板
        Template temp = cfg.getTemplate("myTemplate", "utf-8");
        Writer out = new StringWriter();
        temp.process(data, out);
        return out.toString();
    }


    /**
     * 将文件流写入文件中
     * @param is
     */
    private static File InputStreamToFile(InputStream is) throws Exception{
        File file = File.createTempFile(RandomUtil.randomNumbers(6)+"statistics", ".xls");
        FileOutputStream fos = new FileOutputStream(file);
        byte[] b = new byte[1024];
        while ((is.read(b)) != -1) {
            fos.write(b);// 写入数据
        }
        is.close();
        fos.close();// 保存数据
        return file;
    }

}
生成报表,我这边是把所有的记录都展示,有数据填写,没有数据默认空白

在这里插入图片描述

;