1.需求
按照模板样式导出数据报表,其中每个要导出的数据对象名称不是固定的,需要根据返回数据自动添加,而且每个对象下的选项内容也不是唯一的,需要自行设置大小
2.操作步骤
- 建立一个类似的Excel模板
- 将模板转化成可以编辑状态
将Excel模板另存为 .xml 格式
根据模板内容进行编辑设置
[注:]推荐一个在线格式化网址:https://tool.oschina.net/codeformat/xml
直接将格式化后的文件后缀名改为 .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;
}
}
生成报表,我这边是把所有的记录都展示,有数据填写,没有数据默认空白