关于easyexcel通过模板读取表格的,网上已经有好多了,而且源码里也有详细的demo,这里就不在多说。这里记录一下我的使用。

一:

    目前有个接口是上传Excel并且返回第一行,之前用的是POI,但是数据量大的话,100w条数据就oom了,所以切换到easyexcel,之后内存占用果然小了许多。

二:

    首先引入依赖,目前我用的是2.1.4

 <!-- 快速处理Excel工具-->        
<dependency>            
    <groupId>com.alibaba</groupId>            
    <artifactId>easyexcel</artifactId>            
    <version>2.1.4</version>        
</dependency>

然后创建一个listener,这个需要继承AnalysisEventListener,

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.ehl.etl.ext.utils.JSONArray;
import com.ehl.etl.ext.utils.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
public class ExcelReadListener extends AnalysisEventListener<Map<Integer, String>> {
	private Logger logger = LoggerFactory.getLogger(ExcelReadListener.class);
//	//	 每隔N条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
	private static final int BATCH_COUNT = 3000;
//	自定义用于暂时存储数据,通过Getter在方法中获取
	JSONArray array = new JSONArray();
	/**
	 * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
	 * 如果不重写该方法,默认抛出异常,停止读取
	 *
	 * @param exception
	 * @param context
	 * @throws Exception
	 */
	@Override
	public void onException(Exception exception, AnalysisContext context) {
		logger.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
		if (exception instanceof ExcelDataConvertException) {
			ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
			logger.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
					excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
		}
	}
	/**
	 * 每解析一行会回调invoke()方法。
	 * 如果当前行无数据,该方法不会执行,
	 * 也就是说如果导入的的excel表无数据,该方法不会执行,
	 * 不需要对上传的Excel表进行数据非空判断
	 *
	 * @param map  当前读取到的行数据
	 * @param analysisContext 定义了获取读取excel相关属性的方法
	 */
	@Override
	public void invoke(Map<Integer, String> map, AnalysisContext analysisContext) {
//		目前只需要表格第一行,这里直接跳过
		return;
	}
	/**
	 * 这里会一行行的返回头,每个sheet只返回一次
	 *
	 * @param headMap
	 * @param context
	 */
	@Override
	public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
		logger.info("解析到一条头数据:{}", headMap.toString());
		for (int i = 0; i < headMap.size(); i++) {
			JSONObject object = new JSONObject();
			object.put("name","c_"+(i+1));
			object.put("type","String");
			if (headMap.get(i) == null) {
				object.put("desc","");
			} else {
				object.put("desc",headMap.get(i));
			}
			array.add(object);
		}
	}
	/**
	 * 解析监听器
	 * 每个sheet解析结束会执行该方法
	 */
	@Override
	public void doAfterAllAnalysed(AnalysisContext analysisContext) {
		logger.info("/*------- 当前sheet读取完毕-------*/");
//		如果要在方法中拿到值,这里就不能清除,
//		array.clear();
	}
	public JSONArray getArray() {
		return array;
	}
	public void setArray(JSONArray array) {
		this.array = array;
	}
}

然后就是我们的主类了

 InputStream inputStream = file.getInputStream();
        BufferedInputStream bufferedInputStream = new BufferedInputStream(file.getInputStream());
        ExcelReadListener listener = new ExcelReadListener();
        ExcelReader excelReader = EasyExcelFactory.read(inputStream, listener).build();
//        直接读取第一个sheet,从0开始
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        excelReader.read(readSheet);
//        可以通过getter获取到listener中暂存的值,
        JSONArray data = listener.getArray();
//         这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        excelReader.finish();

 这样就不需要建立模板直接获取内容了。

 

有一点要注意:

这里默认是1,就是0行为头,从第一行开始读取内容,如果将这里指定为0的话,invokeHeadMap就拿不到值了。

 

三:

    执行顺序是先执行invokeHeadMap然后是invoke遍历每一行,最后执行doAfterAllAnalysed结束,这里特别注意,如果在doAfterAllAnalysed中清除了暂存的值,那么在主函数中是拿不到的。

四:

    如果在使用easyexcel时遇到这种问题,就是和POI的依赖冲突了。目前所知的2.1.4版本无法和3.14的POI一起存在,3.17版POI则没有冲突

    关于这个工具我只是大概了解了一下,这个invoke方法如果有值的话每行都会调用它,那么如果我想只获取一行,但是数据量又特别大的时候,就会调用好多次invoke,造成效率低下。如果有人有好的方法欢迎留言。

GitHub 加速计划 / ea / easyexcel
31.63 K
7.47 K
下载
快速、简洁、解决大文件内存溢出的java处理Excel工具
最近提交(Master分支:1 个月前 )
c42183df Bugfix 1 个月前
efa7dff6 * 重新加回 `commons-io` 1 个月前
Logo

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

更多推荐