1 场景
本文主要对EasyExcel
的功能进行简单封装,实现我们在读取excel
中的简单操作。
实现以下功能,能满足大多数情况下的Excel文件读取(03版本、07版本+大数据量导入):
(1)获取列索引对应表头
(第一行为表头),获取表头主要用于判断
(2)以字符串
的形式获取单元格内容
,形式为(列号,单元格内容)
2 版本
EasyExcel:2.2.7
3 maven依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.7</version>
</dependency>
4 代码
4.1 数据处理器接口
读取每xxx条
(默认1000条)数据,调用一次数据处理接口
package com.sa.example.easyexcel.util;
import java.util.List;
import java.util.Map;
/**
* 数据处理器
*/
public interface BaseProcessor {
/**
* 回调方法
* @param list
*/
void invoke(List<Map<Integer,String>> list);
}
4.2 工具类
(1)工具类方法说明如下:
- 读取excel表头
// 读取前多少条的表头(第一行数据),封装到List<String>中返回
public static List<String> getExcelHead(InputStream inputStream, Integer headSize);
// 获取所有有内容的表头数据
public static List<String> getExcelHead(InputStream inputStream);
- 读取excel数据
// 读取1000条数据后,调用一次数据处理器BaseProcessor
public static long readData(InputStream inputStream, BaseProcessor baseProcessor);
// 读取指定条数据batchSize,调用一次数据处理器BaseProcessor
public static long readData(InputStream inputStream, BaseProcessor baseProcessor, final Integer batchSize);
(2)完整工具类代码如下:
package com.sa.example.easyexcel.util;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
/**
* Excel工具类
*/
public class ExcelUtil {
/**
* 行号索引
*/
public static final Integer ROW_NUMBER_INDEX = -1;
private ExcelUtil() {
}
/**
* 读取Excel表头
* @param inputStream 输入流
* @param headSize excel表头个数(指定返回的个数)
* @return
*/
public static List<String> getExcelHead(InputStream inputStream, Integer headSize) {
List<String> excelHeadList = getExcelHead(inputStream);
if (headSize != null && headSize > 0 && CollectionUtils.isNotEmpty(excelHeadList) && excelHeadList.size() >= headSize) {
return excelHeadList.subList(0, headSize);
}
return excelHeadList;
}
/**
* 读取Excel表头
* @param inputStream 输入流
* @return excel表头
*/
public static List<String> getExcelHead(InputStream inputStream) {
// 表头名称列表
final List<String> headList = new ArrayList<>();
//继续执行标志
final AtomicBoolean execFlag = new AtomicBoolean(true);
EasyExcel.read(inputStream, new AnalysisEventListener<Map<Integer, String>>() {
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
if (MapUtils.isNotEmpty(headMap)) {
for (Map.Entry<Integer, String> entry : headMap.entrySet()) {
headList.add(entry.getValue());
}
}
// 终止读取
execFlag.set(false);
}
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
// Do nothing
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// Do nothing
}
@Override
public boolean hasNext(AnalysisContext context) {
return execFlag.get();
}
}).sheet().doRead();
return headList;
}
/**
* 读取数据 (默认1000)
* @param inputStream 文件流
* @param baseProcessor 自定义处理器
* @return 读取数据总数量
*/
public static long readData(InputStream inputStream, BaseProcessor baseProcessor) {
return readData(inputStream, baseProcessor, 1000);
}
/**
* 读取数据
* @param inputStream 文件流
* @param baseProcessor 处理器
* @param batchSize 单批次处理数量
* @return 读取数据总数量
*/
public static long readData(InputStream inputStream, BaseProcessor baseProcessor, final Integer batchSize) {
// 数据行数
final AtomicLong dataCount = new AtomicLong(0L);
EasyExcel.read(inputStream, new AnalysisEventListener<Map<Integer, String>>() {
/**
* 缓存集合
*/
private List<Map<Integer, String>> list = new ArrayList<>(batchSize);
/**
* 当前行号
*/
private Integer lineNumber = 1;
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
lineNumber++;
// 记录总行数
dataCount.addAndGet(1);
if (list.size() >= batchSize) {
// 批量执行任务
if (baseProcessor != null) {
baseProcessor.invoke(list);
}
// 清除缓存
list.clear();
} else {
// 记录行号
data.put(ROW_NUMBER_INDEX, String.valueOf(lineNumber));
// 写入缓存表
list.add(data);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if (baseProcessor != null) {
baseProcessor.invoke(list);
}
}
}).sheet().doRead();
return dataCount.get();
}
}
5 使用
使用到的excel文件《电话区域表.xlsx》如下:
电话区域表.xlsx.png5.1 测试代码
这里设定,每读取6条记录,执行一次
数据处理。
// 声明要读取的文件
File file = new File("D:\\电话区域表.xlsx");
// ==========【一、读取表头】==========
final List<String> headList = new ArrayList<>();
try (InputStream inputStream = new FileInputStream(file)) {
headList.addAll(ExcelUtil.getExcelHead(inputStream));
System.out.println("获取表头:" + headList + "\n");
} catch (Exception e) {
e.printStackTrace();
}
// ==========【二、读取文件内容】==========
try (InputStream inputStream = new FileInputStream(file)) {
long readDataCount = ExcelUtil.readData(inputStream, list -> {
System.out.println("----------开始调用数据处理----------");
// 持久化存储
List<Map<String, Object>> dbDataMapList = new ArrayList<>();
int headSize = headList.size();
for (Map<Integer, String> dataMap : list) {
Map<String, Object> dbDataMap = new HashMap<>(headSize + 1);
// 行号
dbDataMap.put("行号", Integer.valueOf(dataMap.get(ExcelUtil.ROW_NUMBER_INDEX)));
// 导入数据
for (int i = 0; i < headSize; i++) {
dbDataMap.put(headList.get(i), dataMap.get(i));
}
System.out.println("获取数据:" + dbDataMap);
}
}, 6);
System.out.println("\n----------读取数据总数量:" + readDataCount + "----------");
} catch (Exception e) {
e.printStackTrace();
}
5.2 输出内容
获取表头:[前缀号段, 手机所在省份, 手机所在城市, 服务商]
----------开始调用数据处理----------
获取数据:{前缀号段=1300000, 手机所在城市=济南, 行号=2, 手机所在省份=山东, 服务商=中国联通}
获取数据:{前缀号段=1300001, 手机所在城市=常州, 行号=3, 手机所在省份=江苏, 服务商=中国联通}
获取数据:{前缀号段=1300002, 手机所在城市=巢湖, 行号=4, 手机所在省份=安徽, 服务商=中国联通}
获取数据:{前缀号段=1300003, 手机所在城市=宜宾, 行号=5, 手机所在省份=四川, 服务商=中国联通}
获取数据:{前缀号段=1300004, 手机所在城市=自贡, 行号=6, 手机所在省份=四川, 服务商=中国联通}
获取数据:{前缀号段=1300005, 手机所在城市=西安, 行号=7, 手机所在省份=陕西, 服务商=中国联通}
----------开始调用数据处理----------
获取数据:{前缀号段=1300007, 手机所在城市=西安, 行号=9, 手机所在省份=陕西, 服务商=中国联通}
获取数据:{前缀号段=1300008, 手机所在城市=武汉, 行号=10, 手机所在省份=湖北, 服务商=中国联通}
获取数据:{前缀号段=1300009, 手机所在城市=西安, 行号=11, 手机所在省份=陕西, 服务商=中国联通}
----------读取数据总数量:10----------
网友评论