记录一下上周弄的一个模版导出,解决导出样式难以控制的问题。
常规导出,需要人为的判定各种样式,遇到表头合并之类的更麻烦,于是就想到现在比较火的模版导出。
然后,网上的资料大多都是复制粘贴,甚至直接就是转载,而且大多都是直接指定保存路劲,没有实现导出下载功能,这里我记录下自己的一些操作步骤,希望对大家有用。
第一步:导出freemarker的依赖(我的所有项目都是基于maven)
<!-- freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker-version}</version>
</dependency>
第二步:构建导出后的模版(.ftl文件,例如:export.ftl)(按照习惯,在根目录创建一个template文件夹存放模版文件)
<table border="1">
<tr>
<th class="text-center" rowspan="2">序号</th>
<th class="text-center" rowspan="2">姓名</th>
<#if docTypeNums??&&docTypeNums?size>0>
<#list docTypeNums as obj>
<th class="text-center" colspan="${obj["num"]!}">${obj["type_name"]!}</th>
</#list>
</#if>
</tr>
<tr>
<#if docTypes??>
<#list docTypes as obj>
<th class="text-center">${obj.typeName!}</th>
</#list>
</#if>
</tr>
<tbody>
<#if obj??&&(obj?size>0)>
<#list obj?keys as key>
<tr>
<td>${1+key_index}</td>
<td>${obj[key][key]!}(${key})</td>
<#assign map = obj[key]>
<#if docTypes??>
<#list docTypes as ob>
<td>${map[ob.id]!"0"}</td>
</#list>
</#if>
</tr>
</#list>
</#if>
</tbody>
</table>
一个模版用一个table进行构建,当然,如果你又特殊需求,可以使用多个table进行构建。
如果不懂freemarker标签的可以百度,这里我不解释了,我的业务有特殊需求,所以不用看我的数据是怎么操作的,看不懂的(看懂也没用)。
第三步:获取数据(上面模版中的参数,均由map传递)
Map<String,Object> map1 = new HashMap<>();
map1.put("docTypes",docTypes);
map1.put("obj",maps);
map1.put("docTypeNums",docTypeNums);
第四步:渲染模版并导出(渲染时,freemarker会将map解析,获取每一个key,在模版中,我们直接调用key进行操作就可以了)
/***
* 通用导出Excel模版
* @param map 模版需要的数据
* @param filePath 模版所在项目路劲,如:"\\template\\statistics",表示在项目根路劲去找该路劲
* @param fileName 文件名
* @param response 用于输出
* @param request 用于获取项目根目录
* @throws Exception
*/
public void exportExcel(Map<String, Object> map,String filePath,String fileName,HttpServletResponse response,HttpServletRequest request) throws Exception{
//创建一个文件流
File file = new File(request.getSession().getServletContext().getRealPath("/")+filePath);
//初始化freemarker模版引擎的设置
Configuration configuration = new Configuration();
<span style="white-space:pre"> configuration.setDefaultEncoding("UTF-8");</span>
configuration.setDirectoryForTemplateLoading(file);
//获取模版
Template template = configuration.getTemplate(fileName+".ftl","UTF-8");<span style="font-family: Arial, Helvetica, sans-serif; color: rgb(169, 183, 198);"></span>
//创建输出文件流
File outfile = new File(request.getSession().getServletContext().getRealPath("/")+filePath+"/"+fileName+".xlsx");
//创建输出流
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outfile),"UTF-8"));
//渲染模版
template.process(map, out);
out.flush();
out.close();
//用response进行导出
String fileName1 = fileName+".xlsx";
response.setCharacterEncoding("UTF-8");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName1, "UTF-8"));
/*创建输入流*/
FileInputStream in = new FileInputStream(outfile);
BufferedInputStream buff = new BufferedInputStream(in);
IOUtils.copy(buff, response.getOutputStream());
response.flushBuffer();
in.close();
outfile.delete();
}
最后总结:这种方式有一个弊端,就是每次都会在服务器保存一个文件,最后由程序去删除。如果能够做到临时文件流存储,那就圆满了,大大减少服务器的负担。我对文件流不熟悉,如果有大神能够搞定这个功能,还请不吝赐教。