前言

JAVA解析Excel工具EasyExcel的初次使用
easyExcel git地址:https://github.com/alibaba/easyexcel

一、EasyExcel

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便

二、背景及解决

本公司一个相对时间较长的一个SpringBoot项目,系统原有导入Excel偶尔会发生OOM,原有导入功能对导入excel大小限制进行规避。
业务新需求中,涉及到了excel的导入,趁此机会使用easyExcel,为调整原有导入发生OOM做好准备。
由于原项目中使用的POI版本未3.16,此次升级POI 3.17(不升级easyExcel异常)
升级方法调整借鉴:https://www.csdn.net/tags/OtDacg4sNzM2NTUtYmxvZwO0O0OO0O0O.html

主要问题

系统使用人员将excel导入后,需要立即知道结果;若数据落库过程中出现了异常,使用人员需要通过系统反馈进行excel内容调整后再次操作。
但是重写easyExcel是未对异常进行处理的, 同时本次导入Excel及其简单且每次导入数据量有限,所以将excel中所有的数据取出后,统一验证,若有问题再进行抛出。

导入excel校验返回

代码如下:



	public Map importData(@RequestParam(value = "filename") MultipartFile file) {
	
		ExcelReader excelReader = null;
		List<InteractiveUploadDto> deleteList = new ArrayList<>();
		List<InteractiveUploadDto> addList = new ArrayList<>();

		// 数据读取异常统一抛出
		try {
			excelReader = EasyExcelFactory.read(file.getInputStream(), InteractiveUploadDto.class,
					new InteractiveUploadListener( deleteList,addList)).build();
			ReadSheet readSheet = EasyExcelFactory.readSheet(0).build();
			excelReader.read(readSheet);
		} catch (MyException e) {
			throw new MyException("导入Excel识别异常"+e.getMessage());
		}finally {
			if (excelReader != null){
				excelReader.finish();
			}
		}
		// 异常放在了interactiveRemindInfoService捕捉处理
		// 将excel中所有的数据读取到了list内, 然后在service层处理时,如果有异常会throw到页面
		int save = interactiveRemindInfoService.excelDataSave(addList);
		int delete = interactiveRemindInfoService.excelDataDelete(deleteList);


		return getSuccessMap("数据处理成功,保存成功记录数:"+save+" 条后删除成功记录数:"+delete+" 条,请仔细核对!");
	}

public class InteractiveUploadListener extends AnalysisEventListener<InteractiveUploadDto> {
    private static final Logger LOGGER = LoggerFactory.getLogger(InteractiveUploadListener.class);

    private List<InteractiveUploadDto> deleteList ;
    private List<InteractiveUploadDto> addList ;

	// 通过构造参数,将excel中的数据通过业务区分,并返回到相应的service。  
	//业务也可以在此类中进行处理。异常沟通构造参数传回service层,然后再抛出
    public InteractiveUploadListener(List<InteractiveUploadDto> deleteList, List<InteractiveUploadDto> addList){
        this.deleteList = deleteList;
        this.addList = addList;

    }

    @Override
    public void invoke(InteractiveUploadDto vo, AnalysisContext analysisContext) {
        LOGGER.info("收数据导入,解析第{}行数据:{}" , analysisContext.readRowHolder().getRowIndex() , vo);
        // 由于本功能导入数据量肯定不大,所以这里就不在进行分批处理了。 (不建议如此处理 OOM)
        this.buildEntity(vo);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        LOGGER.info("数据处理完成");
    }

    /**
     * @desc        将每行数据,通过业务类型进行区分放入不同的集合中
     * @param dto : excel内的每行数据信息
     */
    private void buildEntity(InteractiveUploadDto dto) {
        // 将每行数据进行区分, 删除操作放入deleteList集合内, 新增放入addList集合内
        if (dto.getDeleteOrNot().equals("Y")){
            deleteList.add(dto);
        }else {
            addList.add(dto);
        }
    }

}
/**
 * @desc 
 * ExcelProperty与导入的Excel一一对应
 */
public class InteractiveUploadDto {
    @ExcelProperty(value = "合同号")
    @NotBlank(message = "不能为空")
    private String applicationNumber;

    @ExcelProperty(value = "待分配用户")
    private String username;

    @ExcelProperty(value = "是否删除")
    @NotBlank(message = "是否删除列不能为空")
    private String deleteOrNot;

    private Long userId;
}

由于本次功能较为简单,数据未进行批次处理,同时我是将业务处理在excel数据解析后,返回了service处理, 在数据处理过程中或校验时,有异常会直接抛到前端。

文件导出

待补充


总结

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐