两种读对象的方式
- 确定表头:建立对象,和表头形成映射关系。
- 不确定表头:每一行数据映射为
Map<String, Object>
,比如用户自己上传的表格。
两种读取模式
- 监听器:先创建监听器、在读取文件时绑定监听器。单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。
- 同步读:无需创建监听器,一次性获取完整数据。方便简单,但是数据量大时会有等待时长,造成卡顿,也可能出现内存溢出。
Easy Excel 基本使用
Easy Excel官网
- 导入依赖
<!-- easy Excel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.0</version></dependency>
- 需要读取的类对象:
package com.heo.matchmatebackend.easyExcel;import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;/*** 用户表格信息*/
@Data
public class TableUserInfo {/*** 用户编号*/@ExcelProperty("用户编号") // 列名匹配(或者也可以用列数 index=0 的方式进行匹配第一列)private String userCode;/*** 用户昵称*/@ExcelProperty("用户昵称")private String username;
}
- 读取监听器
package com.heo.matchmatebackend.easyExcel;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import lombok.extern.slf4j.Slf4j;// 有个很重要的点 TableListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class TableListener implements ReadListener<TableUserInfo> {/*** 这个每一条数据解析都会来调用** @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(TableUserInfo data, AnalysisContext context) {System.out.println(data);}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {System.out.println("已解析完成");}}
- 两种读取数据的方法
package com.heo.matchmatebackend.easyExcel;import com.alibaba.excel.EasyExcel;import java.util.List;/*** 导入Excel,读取数据*/
public class ImportExcel {/*** 读取数据*/public static void main(String[] args) {// 写法1:JDK8+ ,不用额外写一个DemoDataListener// since: 3.0.0-beta1// Excel数据文件放在自己电脑上,能够找到的路径String fileName = "C:\\Codefield\\MY_PROJECT\\match-mate-backend\\src\\main\\resources\\testExcel.xlsx";
// readByListener(fileName);synchronousRead(fileName);}/*** 监听器读取* @param fileName*/public static void readByListener(String fileName) {// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭// 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行EasyExcel.read(fileName, TableUserInfo.class, new TableListener()).sheet().doRead();}/*** 同步读* 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面,且响应时间太久*/public static void synchronousRead(String fileName) {// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finishList<TableUserInfo> list = EasyExcel.read(fileName).head(TableUserInfo.class).sheet().doReadSync();for (TableUserInfo tableUserInfo : list) {System.out.println(tableUserInfo);}}
}
- 实操案例
package com.heo.matchmatebackend.easyExcel;import com.alibaba.excel.EasyExcel;
import org.apache.commons.lang3.StringUtils;import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;public class ImportUser {public static void main(String[] args) {//Excel数据文件放在自己电脑上,能够找到的路径String fileName = "C:\\Codefield\\MY_PROJECT\\match-mate-backend\\src\\main\\resources\\testExcel.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finishList<TableUserInfo> userInfoList =EasyExcel.read(fileName).head(TableUserInfo.class).sheet().doReadSync();System.out.println("总数 = " + userInfoList.size());// 过滤username为空的信息,并分组为 map<列名,列数据的list>Map<String, List<TableUserInfo>> listMap =userInfoList.stream().filter(userInfo -> StringUtils.isNotEmpty(userInfo.getUsername())).collect(Collectors.groupingBy(TableUserInfo::getUsername));for (Map.Entry<String, List<TableUserInfo>> stringListEntry : listMap.entrySet()) {if (stringListEntry.getValue().size() > 1) {System.out.println("username = " + stringListEntry.getKey());
// System.out.println("1");}}System.out.println("不重复昵称数 = " + listMap.keySet().size());}
}