工作遇到一种情况,在导入excel的时候数量过多,导致占用内存太大最终OOM.为了避免这样的情况再次出现,更换easyPoi为EasyExcel,它是一行一行读,非常节省内存且快速.
首先依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel-core</artifactId>
<version>3.2.1</version>
</dependency>
然后java对象,注意该java对象的顺序要跟excel的列顺序完全一一对应,不需要@Excel注解,该注解是之前easyPoi的遗留,我删除了一些无用字段,反正不重要
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ClientImportDTO {
@Excel(name = "客户姓名")
private String name;
@Excel(name = "手机号")
private String phone;
@Excel(name = "获客渠道")
private String source;
@Excel(name = "微信号")
private String wechat;
@Excel(name = "客户星级")
private String level;
@Excel(name = "意向金额")
private String wantAmount;
@Excel(name = "贷款目的")
private String purpose;
@Excel(name = "性别")
private String gender;
}
然后是重点,EasyExcel采用Listener来对excel进行操作
@Slf4j
public class ClientImportDTOListener implements ReadListener<ClientImportDTO> {
//这是队列长度,达到这个数量的时候就结束一波,这里数量过大依然会占用内存,正常50,100就可以
private static final int BATCH_COUNT = 50;
private List<ClientImportDTO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
@Getter
private List<ClientImportDTO> allDataList = new ArrayList<>();
//这里是一条一条解析数据,然后加到上面的list中
@Override
public void invoke(ClientImportDTO data, AnalysisContext context) {
//log.info("解析到一条数据:{}", JSON.toJSONString(data));
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_COUNT) {
allDataList.addAll(cachedDataList);
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
//这里是解析完成之后执行的内容,比如存库就可以直接在这里存,但是因为我要做其他操作所以是做了一个getAllList的操作,全部拿到了再去做其他事情
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
allDataList.addAll(cachedDataList);
log.info("所有数据解析完成,收集到 {} 条数据", allDataList.size());
}
}
重点基本都在代码注释中.然后是最后的controller方法
@ApiOperation("个人线索excel导入")
@PostMapping("/importClue")
public R importClue(MultipartFile file) throws Exception {
long start = System.currentTimeMillis();
// 使用 EasyExcel 读取数据
ClientImportDTOListener listener = new ClientImportDTOListener();
EasyExcel.read(file.getInputStream(), ClientImportDTO.class, listener).sheet().doRead();
// 获取所有数据
List<ClientImportDTO> userList = listener.getAllDataList();
long end = System.currentTimeMillis();
log.info("当前导入的个人线索列表数量为{}, 耗时{}毫秒", userList.size(), end - start);
// 调用 importBatch2 方法处理业务逻辑
return clueService.importBatch2(userList);
}
完事.实测非常优秀,3000条数据仅需100多毫秒