背景
在当前的项目中,有一个需求是数据集文件的预览操作,既然是数据集,大数据量Excel文件也是不可避免的,几百列几万行数据那都是很正常的,因此需要做一个限定行列的都区方案。
因为只读前几行前几列,资源占用和读取时长都很短,因此想要直接同步读进行处理,但是EsayExcel同步读的时候,除了自己重写一些类之外,是默认注册了同步读监听器的,因此之前考虑用同步读一直没实现。
后来在Github下给作者大佬发了个ISSUES,JiaJu Zhuang 大佬给出提示,抛出ExcelAnalysisStopException异常,因此顺着这个思路来搞一下。
实现
监听器
因为同步读存在的制约,因此还是使用异步读,首先定义一个监听器类,实现异步读时的行列限制,这里定义LimitExcelReadListener并继承AnalysisEventListener;
因为我做的是数据集预览,行列均不固定,只能使用Map来接收行,代码不复杂,我直接给出完整代码:
public class LimitExcelReadListener extends AnalysisEventListener<Map<Integer, String>> {
// 定义变量,分别表示限制列数和行数
private Integer limitColSize;
private Integer limitRowSize;
// 定义变量,存储表头信息和表数据
private List<Map<Integer, String>> headList = new ArrayList<>();
private List<Map<Integer, String>> dataList = new ArrayList<>();
// 构造函数
LimitExcelReadListener() {
}
// 带参构造函数,直接赋值限制行列
LimitExcelReadListener(Integer limitColSize, Integer limitRowSize) {
this.limitColSize = limitColSize;
this.limitRowSize = limitRowSize;
}
/**
* 工具方法,根据传入的列限制来保留数据,也就是只取前几个Key,如果有需求,可以自己添加KeySet排序之后再取,我这里没这个需求
* @param oldMap 未限制列前的Map
* @return 限制列后的Map
*/
private Map<Integer, String> getLimitColMap(Map<Integer, String> oldMap) {
Integer size = oldMap.keySet().size();
Map<Integer, String> newMap = new HashMap<>();
for (int i = 0; i < (size >= this.limitColSize ? this.limitColSize : size); i++) {
newMap.put(i, oldMap.get(i));
}
return newMap;
}
@Override
public void invoke(Map<Integer, String> integerStringMap, AnalysisContext analysisContext) {
// 获取限制列之后的Map,并存入dataList
Map<Integer, String> newMap = this.getLimitColMap(integerStringMap);
dataList.add(newMap);
// 判断行数已达到限制行数,抛出ExcelAnalysisException
if (dataList.size() >= this.limitRowSize) {
throw new ExcelAnalysisException(this.limitRowSize + "行" + this.limitColSize + "列读取完成");
}
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
// 获取限制列之后的表头Map,并存入headList
Map<Integer, String> newMap = this.getLimitColMap(headMap);
headList.add(newMap);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
读取工具类
接着是读取的工具类,代码也很简单,这里直接贴出来:
public class EExcelPrevUtils {
/**
* @param path 要读取文件的路径
* @param limitColSize 要限制的列数
* @param limitRowSize 要限制的行数
* @return 读取到的数据
* @throws FileNotFoundException
*/
public static Map<String, Object> readLimitExcel(String path, Integer limitColSize, Integer limitRowSize) throws Exception {
//定义一个Map保存表头和数据
Map<String, Object> result = new HashMap<>();
//声明读取所需FileInputStream
FileInputStream inputStream = new FileInputStream(new File(path));
//初始化一个监听器
LimitExcelReadListener dataListener = new LimitExcelReadListener(limitColSize, limitRowSize);
//读取文件数据
try {
EasyExcel.read(inputStream, dataListener).sheet().doRead();
} catch (ExcelAnalysisException a) {
System.out.println("读取完成");
result.put("headList", dataListener.getHeadList());
result.put("dataList", dataListener.getDataList());
} catch (Exception a) {
System.out.println("读取失败");
throw a;
}
return result;
}
}
其他说明
以上代码都是基于一个Sheet的前提下做的,如果是多个sheet,就根据自己的需求重新搞下读取工具类即可