我也是凑巧用上了,就在这里分享一下,性能确实是没得说!
对easyexcel的介绍是:Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
1、导入的pom.xml
com.alibaba
easyexcel
1.0.3
实际上版本(1.0.1
)在读取的时候是有BUG的,读取03版的.xls
格式的excel正常,但是读取07版的.xlsx
版的excel就会出异常,原因是在解析的时候sheet临时文件路径拼装有误,所以我们用1.0.3
2、使用easyexcel读取excel的时候需要设置excel的版本,但是有些时候我们无法预知excel的版本,所以在网上copy了大神模仿poi
写了一个用于获取com.alibaba.excel.ExcelReader
对象的工具类
package easyExcel.com.easy.excel;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import org.apache.poi.EmptyFileException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.poifs.filesystem.DocumentFactoryHelper;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.util.IOUtils;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.read.context.AnalysisContext;
import com.alibaba.excel.read.event.AnalysisEventListener;
import com.alibaba.excel.support.ExcelTypeEnum;
public class ExcelReaderFactory {
/**
* @param in
* 文件输入流
* @param customContent
* 自定义模型可以在
* {@link AnalysisEventListener#invoke(Object, AnalysisContext) }
* AnalysisContext中获取用于监听者回调使用
* @param eventListener
* 用户监听
* @throws IOException
* @throws EmptyFileException
* @throws InvalidFormatException
*/
public static ExcelReader getExcelReader(InputStream in, Object customContent,
AnalysisEventListener> eventListener) throws EmptyFileException, IOException, InvalidFormatException {
// 如果输入流不支持mark/reset,需要对其进行包裹
if (!in.markSupported()) {
in = new PushbackInputStream(in, 8);
}
// 确保至少有一些数据
byte[] header8 = IOUtils.peekFirst8Bytes(in);
ExcelTypeEnum excelTypeEnum = null;
if (NPOIFSFileSystem.hasPOIFSHeader(header8)) {
excelTypeEnum = ExcelTypeEnum.XLS;
}
if (DocumentFactoryHelper.hasOOXMLHeader(in)) {
excelTypeEnum = ExcelTypeEnum.XLSX;
}
if (excelTypeEnum != null) {
return new ExcelReader(in, excelTypeEnum, customContent, eventListener);
}
throw new InvalidFormatException("Your InputStream was neither an OLE2 stream, nor an OOXML stream");
}
/**
* @param in
* 文件输入流
* @param customContent
* 自定义模型可以在
* {@link AnalysisEventListener#invoke(Object, AnalysisContext) }
* AnalysisContext中获取用于监听者回调使用
* @param eventListener
* 用户监听
* @param trim
* 是否对解析的String做trim()默认true,用于防止 excel中空格引起的装换报错。
* @throws IOException
* @throws EmptyFileException
* @throws InvalidFormatException
*/
public static ExcelReader getExcelReader(InputStream in, Object customContent,
AnalysisEventListener> eventListener, boolean trim)
throws EmptyFileException, IOException, InvalidFormatException {
// 如果输入流不支持mark/reset,需要对其进行包裹
if (!in.markSupported()) {
in = new PushbackInputStream(in, 8);
}
// 确保至少有一些数据
byte[] header8 = IOUtils.peekFirst8Bytes(in);
ExcelTypeEnum excelTypeEnum = null;
if (NPOIFSFileSystem.hasPOIFSHeader(header8)) {
excelTypeEnum = ExcelTypeEnum.XLS;
}
if (DocumentFactoryHelper.hasOOXMLHeader(in)) {
excelTypeEnum = ExcelTypeEnum.XLSX;
}
if (excelTypeEnum != null) {
return new ExcelReader(in, excelTypeEnum, customContent, eventListener, trim);
}
throw new InvalidFormatException("Your InputStream was neither an OLE2 stream, nor an OOXML stream");
}
}
3、然后编写一个简单的测试类
package easyExcel.com.easy.excel;
import java.io.FileInputStream;
import java.io.InputStream;
import org.junit.Test;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.read.context.AnalysisContext;
import com.alibaba.excel.read.event.AnalysisEventListener;
public class ExcelWriteTest {
public static class ExcelPropertyIndexModel extends BaseRowModel {
@ExcelProperty(value = "姓名", index = 0)
private String name;
@ExcelProperty(value = "年龄", index = 1)
private String age;
@ExcelProperty(value = "邮箱", index = 2)
private String email;
@ExcelProperty(value = "地址", index = 3)
private String address;
@ExcelProperty(value = "性别", index = 4)
private String sax;
@ExcelProperty(value = "高度", index = 5)
private String heigh;
@ExcelProperty(value = "备注", index = 6)
private String last;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSax() {
return sax;
}
public void setSax(String sax) {
this.sax = sax;
}
public String getHeigh() {
return heigh;
}
public void setHeigh(String heigh) {
this.heigh = heigh;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
@Override
public String toString() {
return "ExcelPropertyIndexModel [name=" + name + ", age=" + age + ", email=" + email + ", address="
+ address + ", sax=" + sax + ", heigh=" + heigh + ", last=" + last + "]";
}
}
@Test
public void read() throws Exception {
//text.xlsx
try (InputStream in = new FileInputStream("try.xlsx");) {
AnalysisEventListener listener = new AnalysisEventListener() {
@Override
public void invoke(ExcelPropertyIndexModelobject, AnalysisContext context) {
System.err.println("Row:" + context.getCurrentRowNum() + " Data:"
+ object.toString());
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.err.println("doAfterAllAnalysed...");
}
};
ExcelReader excelReader = ExcelReaderFactory.getExcelReader(in, null, listener);
// 第二个参数为表头行数,按照实际设置
excelReader.read(new Sheet(1, 1, ExcelPropertyIndexModel.class));
}
}
}
解释一下测试类的代码:ExcelPropertyIndexModel是一个用来存放解析完表格的数据类,@ExcelProperty(value = "名称", index = 10)注解中的index标识着这个参数对应的列,index从0开始