1、问题描述
最近有一个项目要求导出Excel,但是却不是直接从表中导出,甚至连实体都不能创建,因为这个excel的表头是从数据库中查询,不光内容会变化连个数也不确定
最终我使用的是EasyExcel的动态表头解决这个问题,希望可以帮助到其他人,有用就鼓励一下吧
2、引入EasyExcel
1、创建springBoot工程
2、引入依赖
<!--引入easyExcel-->
<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
2、导入
1、创建监听器,请注意easyExcel的监听器不能交给spring管理
public class EasyExcelListener extends AnalysisEventListener<Map<Integer, String>> {
//表头数据(存储所有的表头数据)
private List<Map<Integer, String>> headList = new ArrayList<>();
//数据体
private List<Map<Integer, String>> dataList = new ArrayList<>();
@Override//这里会一行行的返回头
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
//存储全部表头数据
System.out.println("1");
headList.add(headMap);
}
@Override// 处理每一行数据
public void invoke(Map<Integer, String> data, AnalysisContext context) {
dataList.add(data);
}
@Override// 全部处理结束执行
public void doAfterAllAnalysed(AnalysisContext context) {
}
public List<Map<Integer, String>> getHeadList() {
return headList;
}
public List<Map<Integer, String>> getDataList() {
return dataList;
}
}
2、工具类的工具方法
public class EasyExcelImportUtils {
/**
* 动态表头导入功能
*
* @param file 文件
* @return
*/
public static List<Map<String, String>> importExcel(MultipartFile file) {
try {
// 首先校验传入文件是否为空
if (file == null) {
throw new BookException(2000, "传入数据为空");
}
// 引入监听器(此处需注意,监听器不可被Spring管理)
EasyExcelListener readListener = new EasyExcelListener();
// 开始处理excel
EasyExcelFactory.read(file.getInputStream(), readListener)
.sheet(0)
.doRead();
// 获取表头(验空)
List<Map<Integer, String>> headList = readListener.getHeadList();
if (CollectionUtils.isEmpty(headList)) {
throw new RuntimeException("Excel表头不能为空");
}
// 获取表数据(验空)
List<Map<Integer, String>> dataList = readListener.getDataList();
if (CollectionUtils.isEmpty(dataList)) {
throw new RuntimeException("Excel数据内容不能为空");
}
//获取头部,取最后一次解析的列头数据
Map<Integer, String> excelHeadIdxNameMap = headList.get(headList.size() - 1);
//封装数据体
List<Map<String, String>> excelDataList = new ArrayList<Map<String, String>>();
for (Map<Integer, String> dataRow : dataList) {
HashMap<String, String> rowData = new HashMap<>();
excelHeadIdxNameMap.entrySet().forEach(columnHead -> {
rowData.put(columnHead.getValue(), dataRow.get(columnHead.getKey()));
});
excelDataList.add(rowData);
}
return excelDataList;
} catch (Exception e) {
e.printStackTrace();
throw new BookException(2000, "导入失败");
}
}
}
3、导出
需注意动态导出因为表头不确定,所以需要手动传入格式为List<List<String>>
需要传入数据体格式为List<List<Object>>
要求表头和数据体的数据必须顺序一致,没有数据用0或者null代替,否则就会出现错位问题
public class EasyExcelImportUtils {
/**
* 动态表头导出
*
* @param response HttpServletResponse
* @param fileName 文件名
* @param Myhead Excel表头信息
* @param data Excel数据
*/
public static void exportExcel(HttpServletResponse response, String fileName, List<List<String>> Myhead, List<List<Object>> data) {
try {
//1 设置下载相关内容
//设置mime类型
response.setContentType("application/vnd.ms-excel");
//设置编码
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
fileName = URLEncoder.encode(fileName, "UTF-8");
//设置响应头信息 Content-disposition
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream())
.head(Myhead)
.sheet(fileName)
.doWrite(data);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 有实体的导出
*
* @param response HttpServletResponse
* @param fileName 文件名
* @param data 数据
* @param clazz 实体类型
* @param <T>
*/
public static <T> void exportExcelByEntity(HttpServletResponse response, String fileName, List<T> data, Class<T> clazz) {
try {
//1 设置下载相关内容
//设置mime类型
response.setContentType("application/vnd.ms-excel");
//设置编码
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
fileName = URLEncoder.encode(fileName, "UTF-8");
//设置响应头信息 Content-disposition
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
// 导出
EasyExcel.write(response.getOutputStream(), clazz)
.sheet(fileName)
.doWrite(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
4、注意
导出不可用Swagger测试,使用postman可以导出但是名字会有问题,尽量使用浏览器直接访问,这要求我们请求方式为get,当然也可已使用post请求,只是测试的时候会有一些问题。