背景

在当前的项目中,有一个需求是数据集文件的预览操作,既然是数据集,大数据量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,就根据自己的需求重新搞下读取工具类即可