关于easyexcel通过模板读取表格的,网上已经有好多了,而且源码里也有详细的demo,这里就不在多说。这里记录一下我的使用。
一:
目前有个接口是上传Excel并且返回第一行,之前用的是POI,但是数据量大的话,100w条数据就oom了,所以切换到easyexcel,之后内存占用果然小了许多。
二:
首先引入依赖,目前我用的是2.1.4
com.alibaba
easyexcel
2.1.4
然后创建一个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> {
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(Mapmap, AnalysisContext analysisContext) {
//目前只需要表格第一行,这里直接跳过
return;
}
/**
* 这里会一行行的返回头,每个sheet只返回一次
*
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(MapheadMap, 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,造成效率低下。如果有人有好的方法欢迎留言。