技术选型:ssm框架、数据库搭配的是SQLServer 2008 R2、maven项目。
业务需求:需要导出数据报表、要求格式标题字体颜色等配置。
选用技术:freemarker 可支持多种标签类型、可以自定义表格样式、使用方便简单。
一、
1.首先项目引入 jar 包
<!-- 项目属性 -->
<properties>
<freemarker.version>2.3.20</freemarker.version>
</properties>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>${freemarker.version}</version>
</dependency>
二、
1.新建一个 excel 文件、自定义好样式、如下:
2.讲表格另存为一下、选择文件类型为 “XML” 类型、如下:
3.用浏览器打开会显示如下、个人原因觉得用 Notepad 打开很乱、所以习惯用浏览器打开、然后粘贴到文件中。当然你也可以直接粘贴到文件中、这个随意。
三、
1.回到开发工具项目中建立一个文件夹、专门放 freemarker 的模板。如下:
2.在该文件夹下新建一个 .ftl 文件、把新建好的 xml 文件内容粘贴到 .ftl 文件中。如下:
用 wps excel 生成的 xml 文件没有指定版本。
用 ms excel 生成的 xml 文件头前多两句:
<?xml version="1.0"?> 显示普通的xml版本信息
<?mso-application progid="Excel.Sheet"?> 让Office去打开EXCEL处理XML
这两句话有没有并没有什么影响。
3.简单的说一下 .ftl 文件中的标签。
所有 <style> 都在 <styles> 里面。
<Style ss:ID="s59"> 设定 ss:ID 在需要使用样式的地方调用该 ID 则可,调用方式:<Cell ss:StyleID="s59"/> 使用 ss:StyleID="s59" 则可。
<Data ss:Type="String"> "数据 "</Data>、data标签中写数据、ss:Type 指单元格类型、可以是 String、Number等。
<Worksheet ss:Name="Sheet2"> worksheet 指代工作簿、只需要一个工作簿的话、后面的 worksheet 标签全部删除则可。
4.创建 Util 工具类。一个将 xml 生成 excel 的工具类、一个下载类。如下:
public class TemplateParseUtil {
/**
* 解析模板生成Excel
* @param templateDir 模板目录
* @param templateName 模板名称
* @param path 生成的文件路径
* @param data 数据参数
* @throws IOException
* @throws TemplateException
*/
@SuppressWarnings("deprecation")
public static void parse(String templateDir,String templateName,String path,Map<String,Object> data) throws IOException, TemplateException {
//初始化工作
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;
try{
//填充数据至Excel
writer = new OutputStreamWriter(new FileOutputStream(path),"UTF-8");
template.process(data, writer);
writer.flush();
}finally{
writer.close();
}
}
/**
* 解析模板返回字节数组
* @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();
}
}
public class DownloadFile {
public static void downloadFile(String filePath,HttpServletResponse response){
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
// path是指欲下载的文件的路径。
File file = new File(filePath);
// 取得文件名。
String filename = file.getName();
// 设置response的Header
String downLoadName = new String(filename.getBytes("gbk"), "iso8859-1");
response.setHeader("Content-Disposition", "attachment; filename=" + downLoadName);
response.addHeader("Content-Length", "" + file.length());
response.setContentType("application/octet-stream");
bis = new BufferedInputStream(new FileInputStream(filePath));
bos = new BufferedOutputStream(response.getOutputStream());
byte[] buff = new byte[2048];
int bytesRead;
while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
bos.write(buff, 0, bytesRead);
}
bis.close();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean deleteFile(String filePath){
boolean flag = false;
File file = new File(filePath);
if(file.exists()){
flag = file.delete();
}
return flag;
}
}
5. 封装数据。
1. 使用 map 封装
/**
* 日月报导出
*/
@RequestMapping(value = "exportElectric/{type}")
public void exportElectric(@PathVariable(value = "type")int type,Technology technology, HttpServletRequest request, HttpServletResponse response) throws Exception {
// type == 1 是 日报
String fileName;
Date start = technology.getDate(),end;
if(type == 1){
fileName = "电能消耗统计日报";
if(technology.getDate()==null){
start = Global.getDay(0);
}
Calendar cal = Calendar.getInstance();
cal.setTime(start);
cal.add(Calendar.DATE,1);
end = cal.getTime();
}else{
fileName = "电能消耗统计月报";
if(technology.getDate() == null){
start = new Date();
}
Map mapDate = FactoryDateUtils.monthDate2FactoryDate(start);
Calendar cal = Calendar.getInstance();
cal.setTime((Date) mapDate.get("startDate"));
cal.add(Calendar.DATE,1);
end = cal.getTime();
start = (Date) mapDate.get("endDate");
}
// 获取小班组
List<Technology> technologyList = technologyService.queryTeamList(technology);
Map<String,Object> map = data(technologyList,start,end);
List<Map<String,Object>> reslist = (List<Map<String,Object>>)map.get("list");
// 要导出的数据
Map<String, Object> dataMap = new HashMap<String, Object>();
dataMap.put("clist", reslist);
dataMap.put("dataSize", reslist.size() + 10);
dataMap.put("fileName", fileName);
dataMap.put("zMap", map);
String basePath = request.getSession().getServletContext().getRealPath("/");
// 模板保存路径
String demoPath = basePath + "exportTemp";
// 导出后文件存储位置
String savePath = fileName+".xls";
// 要导出的数据的集合
TemplateParseUtil.parse(demoPath, "test.ftl", savePath, dataMap);
DownloadFile.downloadFile(savePath, response);
DownloadFile.deleteFile(savePath);
}
dataMap:封装好数据
basePath:模板保存路径也就是你创建的 .ftl 文件的位置
savePath:保存文件路径名称
table 标签下的 第一行 row 为标题、可以自定义、也可以从后台传、接收方式 ${}
在你的标题后的 <Row> 标签后、删除多余的 <Row> 标签,做一个循环。
<#list clist as item> </#list>
标签固定写法、clist 是后端封装好的数据名称、这个随意更换。
我的这里 clist 是 List<Map<String,Object>> 格式、取值方式:${item["team"]!} 、后面跟 “!”的作用是解决数据空的问题。
2. 使用 entity 封装
其操作与 Map 封装一直、其中循环方式取参不一样、直接取参则可。如下: