欢迎大家关注本博,同时欢迎大家评论交流,可以给个赞哦!!!
Alibaba Easyexcel 简介:
根据官方文档描述,Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到几M,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便。
Alibaba Easyexcel 工具类整合:
话不多少,直接上干货,在Alibaba Easyexcel 之上进行进一步抽象。
· EasyExcelUtil类
package com.arhorchin.securitit.files.easyexcel;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
/**
* @author Securitit.
* @note Alibaba EasyExcel 封装类.
*/
public class EasyExcelUtil {
/**
* logger.
*/
private static Logger logger = LoggerFactory.getLogger(EasyExcelUtil.class);
/**
* 读取 Excel(多个 sheet).
*
* @param excelBts
* Excel文件.
* @param rowModel
* 实体类映射,继承 BaseRowModel 类.
* @return Excel 数据 list.
*/
public static List<Object> readExcel(byte[] excelBts, BaseRowModel rowModel) {
List<Object> retList = null;
ExcelReader excelReader = null;
EasyExcelListener easyExcelListener = null;
try {
retList = new ArrayList<Object>();
easyExcelListener = new EasyExcelListener();
excelReader = getReader(excelBts, easyExcelListener);
if (null == excelReader) {
return retList;
}
for (Sheet sheet : excelReader.getSheets()) {
if (null != rowModel) {
sheet.setClazz(rowModel.getClass());
}
excelReader.read(sheet);
}
retList = easyExcelListener.getDatas();
} catch (Exception ex) {
logger.error("EasyExcelUtil.readExcel.初始化Excel文件异常.", ex);
}
return retList;
}
/**
* 读取某个 sheet 的 Excel.
*
* @param excelBts
* Excel文件.
* @param rowModel
* 实体类映射,继承 BaseRowModel 类.
* @param sheetNo
* sheet 的序号 从1开始.
* @return Excel 数据 list.
*/
public static List<Object> readExcel(byte[] excelBts, BaseRowModel rowModel, int sheetNo) {
return readExcel(excelBts, rowModel, sheetNo, 1);
}
/**
* 读取某个 sheet 的 Excel.
*
* @param excelBts
* Excel文件.
* @param rowModel
* 实体类映射,继承 BaseRowModel 类.
* @param sheetNo
* sheet 的序号 从1开始.
* @param headLineNum
* 表头行数,默认为1.
* @return Excel 数据 list.
*/
public static List<Object> readExcel(byte[] excelBts, BaseRowModel rowModel, int sheetNo, int headLineNum) {
List<Object> retList = null;
ExcelReader excelReader = null;
EasyExcelListener easyExcelListener = null;
try {
retList = new ArrayList<Object>();
easyExcelListener = new EasyExcelListener();
excelReader = getReader(excelBts, easyExcelListener);
if (null == excelReader) {
return retList;
}
excelReader.read(new Sheet(sheetNo, headLineNum, rowModel.getClass()));
retList = easyExcelListener.getDatas();
} catch (Exception ex) {
logger.error("EasyExcelUtil.readExcel.初始化Excel文件异常.", ex);
}
return retList;
}
/**
* 导出 Excel :一个 sheet,带表头.
*
* @param response
* HttpServletResponse.
* @param list
* 数据 list,每个元素为一个 BaseRowModel.
* @param fileName
* 导出的文件名.
* @param sheetName
* 导入文件的 sheet 名.
* @param object
* 映射实体类,Excel 模型.
*/
public static void writeExcel(HttpServletResponse response, List<? extends BaseRowModel> list, String fileName,
String sheetName, BaseRowModel object) {
ExcelWriter writer = new ExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
Sheet sheet = new Sheet(1, 0, object.getClass());
sheet.setSheetName(sheetName);
writer.write(list, sheet);
writer.finish();
}
/**
* 导出 Excel :多个 sheet,带表头.
*
* @param response
* HttpServletResponse.
* @param list
* 数据 list,每个元素为一个 BaseRowModel.
* @param fileName
* 导出的文件名.
* @param sheetName
* 导入文件的 sheet 名.
* @param object
* 映射实体类,Excel 模型.
*/
public static EasyExcelWriterFactroy writeExcelWithSheets(HttpServletResponse response,
List<? extends BaseRowModel> list, String fileName, String sheetName, BaseRowModel object) {
EasyExcelWriterFactroy writer = new EasyExcelWriterFactroy(getOutputStream(fileName, response),
ExcelTypeEnum.XLSX);
Sheet sheet = new Sheet(1, 0, object.getClass());
sheet.setSheetName(sheetName);
writer.write(list, sheet);
return writer;
}
/**
* 导出文件时为Writer生成OutputStream.
*
* @param fileName
* .
* @param response
* .
* @return.
*/
private static OutputStream getOutputStream(String fileName, HttpServletResponse response) {
// 创建本地文件
String filePath = fileName + ".xlsx";
File dbfFile = new File(filePath);
try {
if (!dbfFile.exists() || dbfFile.isDirectory()) {
dbfFile.createNewFile();
}
fileName = new String(filePath.getBytes(), "ISO-8859-1");
response.addHeader("Content-Disposition", "filename=" + fileName);
return response.getOutputStream();
} catch (IOException e) {
throw new EasyExcelException("创建文件失败!");
}
}
/**
* 返回 ExcelReader.
*
* @param excelBts
* Excel文件.
* @param EasyExcelListener
* new EasyExcelListener().
*/
private static ExcelReader getReader(byte[] excelBts, EasyExcelListener easyExcelListener) {
InputStream excelBais = null;
try {
excelBais = new ByteArrayInputStream(excelBts);
return new ExcelReader(excelBais, null, easyExcelListener, false);
} catch (Exception ex) {
logger.error("EasyExcelUtil.getReader.获取Excel文件InputStream异常.", ex);
}
return null;
}
}
· EasyExcelListener类
package com.arhorchin.securitit.files.easyexcel;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
/**
* @author Securitit.
* @note EasyExcel解析监听器.
*/
@SuppressWarnings("rawtypes")
public class EasyExcelListener extends AnalysisEventListener {
/**
* logger.
*/
private static Logger logger = LoggerFactory.getLogger(EasyExcelListener.class);
/**
* 已解析数据存储.
*/
private List<Object> datas = new ArrayList<>();
/**
* 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据.
*/
@Override
public void invoke(Object object, AnalysisContext context) {
// 数据存储到list,供批量处理,或后续自己业务逻辑处理。
datas.add(object);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
logger.info("EasyExcelListener.doAfterAllAnalysed.解析Excel完毕.");
}
public List<Object> getDatas() {
return datas;
}
public void setDatas(List<Object> datas) {
this.datas = datas;
}
}
· EasyExcelWriterFactroy类
package com.arhorchin.securitit.files.easyexcel;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;
/**
* @author Securitit.
* @note EasyExcel 写入.
*/
public class EasyExcelWriterFactroy extends ExcelWriter {
private int sheetNo = 1;
private OutputStream outputStream;
public EasyExcelWriterFactroy(OutputStream outputStream, ExcelTypeEnum typeEnum) {
super(outputStream, typeEnum);
this.outputStream = outputStream;
}
/**
* 写入行数据.
* @param list 数据行.
* @param sheetName sheet页名称.
* @param object . @return.
*/
public EasyExcelWriterFactroy write(List<? extends BaseRowModel> list, String sheetName, BaseRowModel object) {
this.sheetNo++;
try {
Sheet sheet = new Sheet(sheetNo, 0, object.getClass());
sheet.setSheetName(sheetName);
this.write(list, sheet);
} catch (Exception ex) {
ex.printStackTrace();
try {
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
return this;
}
@Override
public void finish() {
super.finish();
try {
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
· EasyExcelException类
package com.arhorchin.securitit.files.easyexcel;
/**
* @author Securitit.
* @note EasyExcel自定义异常.
*/
public class EasyExcelException extends RuntimeException {
/**
* serialVersionUID.
*/
private static final long serialVersionUID = -7099423768629232473L;
public EasyExcelException(String message) {
super(message);
}
}
上面这几个类是读取和写入Excel的工具类,可以复制直接使用。
Alibaba Easyexcel 测试:
测试之前,我们首先编写一Excel,用于存储数据,本文测试使用的Excel如下:
接着,需要新建BaseRowModel的子类,用于匹配Excel中对应数据项,本文使用TestRowModel,如下:
package com.arhorchin.securitit.files.easyexcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
/**
* @author Securitit.
* @note EasyExcelTester RowModel.
*/
public class TestRowModel extends BaseRowModel {
/**
* 序号.
*/
@ExcelProperty(
index = 0)
private String index;
/**
* 姓名.
*/
@ExcelProperty(
index = 1)
private String name;
/**
* 年龄.
*/
@ExcelProperty(
index = 2)
private String gmfdz;
/**
* 住址.
*/
@ExcelProperty(
index = 3)
private String address;
public String getIndex() {
return index;
}
public void setIndex(String index) {
this.index = index;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGmfdz() {
return gmfdz;
}
public void setGmfdz(String gmfdz) {
this.gmfdz = gmfdz;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
最后,通过上面已有内容,在测试中将Excel数据读取到JavaBean中:
package com.arhorchin.securitit.files.easyexcel;
import java.io.File;
import java.util.List;
import org.apache.commons.io.FileUtils;
import com.alibaba.fastjson.JSON;
/**
* @author Securitit.
* @note EasyExcelTester测试类.
*/
public class EasyExcelTester {
public static void main(String[] args) throws Exception {
String excelPath = null;
byte[] excelBytes = null;
TestRowModel rowModel = null;
List<Object> parseObjList = null;
rowModel = new TestRowModel();
excelPath = "K:/files/easyexcel/EasyExcelTester.xlsx";
excelBytes = FileUtils.readFileToByteArray(new File(excelPath));
parseObjList = EasyExcelUtil.readExcel(excelBytes, rowModel);
System.out.println(JSON.toJSONString(parseObjList));
}
}
测试类输出结果:
[{"address":"辽宁","cellStyleMap":{},"gmfdz":"18","index":"1","name":"张三"},{"address":"北京","cellStyleMap":{},"gmfdz":"23","index":"2","name":"李四"},{"address":"四川","cellStyleMap":{},"gmfdz":"43","index":"3","name":"赵五"}]
可以看到,Excel中内容已读取到JavaBean中,其中cellStyleMap属性是BaseRowModel的属性,对实际操作不会产生影响。
总结:
Alibaba Easyexcel的操作相对比较简单,尤其处理大数据时,是首选,可以避免Excel操作给应用带来的压力。
本博微信公众号“超哥说码”,欢迎大家订阅,公众号正在完善中,会及时将更优质的博文推送于您!