使用的是阿里的 EasyExcel ,在读的时候出现NPE错误,追究一下
使用 EasyExcel 写excel的时候 api 非常简单好用,而在读的时候却发现有一些bug。
maven依赖版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.2-beta5</version>
</dependency>
1.1.2-beta5 是当前(2019年3月16日)最新版,然而却不是稳定版,其中1.0.0有稳定版,但官方的github的示例却是 测试版的(1.1.2-beta4),且对前版本兼容较差。在评论中也是bug百出,可见阿里开发这个组件的负责人已经停止维护了。
此篇仅记录该工具类的一个问题
读取文件路径 “F:/excelx.xlsx”
代码:
//从excel中读取
public static List<Object> read(String excelName) throws IOException{
if(StringUtils.isEmpty(excelName))
return null;
try( InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(excelName)) {
// 解析每行结果在listener中处理
return EasyExcelFactory.read(inputStream, new Sheet(1, 0));//该除为41行
}
}
java.lang.NullPointerException
at com.alibaba.excel.support.ExcelTypeEnum.valueOf(ExcelTypeEnum.java:31)
at com.alibaba.excel.ExcelReader.<init>(ExcelReader.java:94)
at com.alibaba.excel.EasyExcelFactory.read(EasyExcelFactory.java:30)
at com.msgc.utils.excel.ExcelUtil.read(ExcelUtil.java:41)
发现是在调用用 read 的时候
一步一步翻源码看,源码在read前调用了 new ExcelReader。
而在该类的 init 方法中第一行调用了
ExcelTypeEnum excelTypeEnum = ExcelTypeEnum.valueOf(in);
源码如下
public static ExcelTypeEnum valueOf(InputStream inputStream) {
try {
if (!inputStream.markSupported()) {//出错点
return null;
} else {
FileMagic fileMagic = FileMagic.valueOf(inputStream);
if (FileMagic.OLE2.equals(fileMagic)) {
return XLS;
} else {
return FileMagic.OOXML.equals(fileMagic) ? XLSX : null;
}
}
} catch (IOException var2) {
throw new RuntimeException(var2);
}
}
(阅读发现阿里的EasyExcel其实是封装了 apache 的 poi ,稍微改了一些地方,让使用起来更加方便,却有一堆修不完的 bug,后来也停止维护了。)
根据堆栈信息,是inputStream.markSupported()方法报的错,点开是 jdk 的源码
/**
* Tests if this input stream supports the <code>mark</code> and
* <code>reset</code> methods. Whether or not <code>mark</code> and
* <code>reset</code> are supported is an invariant property of a
* particular input stream instance. The <code>markSupported</code> method
* of <code>InputStream</code> returns <code>false</code>.
*
* @return <code>true</code> if this stream instance supports the mark
* and reset methods; <code>false</code> otherwise.
* @see java.io.InputStream#mark(int)
* @see java.io.InputStream#reset()
*/
public boolean markSupported() {
return false;
}
翻译过来大概是说这是个标志方法,用来检测输入流是否支持标记和重置方法,jdk 的输入输出流采用了装饰者模式,该处是写死的也不为过。
而我是拷贝了官方给的例子。
总结
使用EasyExcel 读的时候最好多看看源码,不看源码很容易出问题。
本文出现问题解决方案:
//从excel中读取
public static List<Object> read(String excelName) throws IOException{
try(BufferedInputStream is = new BufferedInputStream(new FileInputStream(excelName))) {
if(is == null)
return null;
return EasyExcelFactory.read(is, new Sheet(1, 0));
}
}
装饰器包装一下,而不用示例给的.
Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName);