工作遇到一种情况,在导入excel的时候数量过多,导致占用内存太大最终OOM.为了避免这样的情况再次出现,更换easyPoi为EasyExcel,它是一行一行读,非常节省内存且快速.
首先依赖
java"> <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel-core</artifactId><version>3.2.1</version></dependency>
然后java对象,注意该java对象的顺序要跟excel的列顺序完全一一对应,不需要@Excel注解,该注解是之前easyPoi的遗留,我删除了一些无用字段,反正不重要
java">@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进行操作
java">@Slf4j
public class ClientImportDTOListener implements ReadListener<ClientImportDTO> {
//这是队列长度,达到这个数量的时候就结束一波,这里数量过大依然会占用内存,正常50,100就可以private static final int BATCH_COUNT = 50;private List<ClientImportDTO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Getterprivate List<ClientImportDTO> allDataList = new ArrayList<>();//这里是一条一条解析数据,然后加到上面的list中@Overridepublic 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的操作,全部拿到了再去做其他事情@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {allDataList.addAll(cachedDataList);log.info("所有数据解析完成,收集到 {} 条数据", allDataList.size());}}
重点基本都在代码注释中.然后是最后的controller方法
java"> @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多毫秒