Java处理Excel文件工具包-easyexcel使用详解
https://github.com/alibaba/easyexcel
由于项目需要对大量Excel数据进行输入输出处理,在使用JXL,POI后发现很容易出现OOM,最后在网上找到阿里的开源项目EasyExcel能很快速的读取写入超大Excel文件。经过大量的调试优化,现通过JAVA生成104万行20列的数据并写入到Excel文件的Sheet中只需要70秒的时间。
以下为本工程代码:
如果是maven工程在pom.xml中加入以下内容:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>27.0-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.2-beta5</version>
</dependency>
工具包封装
package utils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author zhao.yingchao
* @date 2019-02-27
* @since v1.0.0
*/
public class EasyExcelUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(EasyExcelUtil.class);
/**
* 通过String类,读取工作表数据
*
* @param filePath 文件路径
* @return 数据集
*/
public static Map<String, List<List<String>>> readExcelByString(String filePath) {
return readExcelByString(filePath, null);
}
/**
* 通过String类,读取工作表数据
*
* @param filePath 文件路径
* @param sheetName sheetName
* @return 数据集合
*/
public static Map<String, List<List<String>>> readExcelByString(String filePath, String sheetName) {
// 创建返回信息
Map<String, List<List<String>>> dataListMap;
// 解析监听器
StringExcelListener excelListener = new StringExcelListener();
InputStream inputStream = null;
try {
// 创建文件流
inputStream = new FileInputStream(filePath);
dataListMap = readExcelByStringFromInputStream(inputStream, sheetName);
} catch (Exception e) {
throw new EasyExcelException("readExcelByModel from filePath failed." + e, e);
} finally {
// 关闭文件流
try {
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
LOGGER.error("inputStream.close failed!", e);
}
}
return dataListMap;
}
/**
* 通过String类,读取工作表数据
*
* @param inputStream 文件流
* @param sheetName sheetName
* @return 数据集合
*/
public static Map<String, List<List<String>>> readExcelByStringFromInputStream(InputStream inputStream,
String sheetName) {
// 创建返回信息
Map<String, List<List<String>>> dataListMap = Maps.newLinkedHashMap();
// 解析监听器
StringExcelListener excelListener = new StringExcelListener();
try {
// 创建文件流
ExcelReader excelReader = EasyExcelFactory.getReader(inputStream, excelListener);
// 得到所有工作表
List<Sheet> sheets = excelReader.getSheets();
// 取所有工作表数据
for (Sheet sheet : sheets) {
// 工作表名称
String currentSheetName = sheet.getSheetName();
// 没有指定工作表,或多个工作表
if (Strings.isNullOrEmpty(sheetName) || Splitter.on(',').trimResults().omitEmptyStrings().splitToList(
sheetName).contains(currentSheetName)) {
// 读取Excel数据
excelReader.read(sheet);
// 返回明细数据
List<List<String>> sheetDataInfo = Lists.newArrayList(excelListener.getDataList());
// 将工作表数据放入工作薄
dataListMap.put(currentSheetName, sheetDataInfo);
// 清除缓存数据
excelListener.clear();
}
}
} catch (Exception e) {
throw new EasyExcelException("readExcelByStringFromInputStream from inputStream failed." + e, e);
}
return dataListMap;
}
/**
* 通过Model类,读取工作表数据
*
* @param filePath 文件路径
* @param clazz BaseRowModel
* @return 数据集合
*/
public static Map<String, List<? extends BaseRowModel>> readExcelByModel(String filePath,
Class<? extends BaseRowModel> clazz) {
return readExcelByModel(filePath, null, clazz);
}
/**
* 通过Model类,读取工作表数据
*
* @param file 文件
* @param clazz BaseRowModel
* @return 数据集合
*/
public static Map<String, List<? extends BaseRowModel>> readExcelByModel(File file,
Class<? extends BaseRowModel> clazz) {
return readExcelByModel(file, null, clazz);
}
/**
* 通过Model类,读取工作表数据
*
* @param filePath 文件路径
* @param sheetName sheetName
* @param clazz BaseRowModel
* @return 数据集合
*/
public static List<? extends BaseRowModel> readExcelByModelSheetName(String filePath, String sheetName,
Class<? extends BaseRowModel> clazz) {
Map<String, List<? extends BaseRowModel>> dataListMap = readExcelByModel(filePath, sheetName, clazz);
return dataListMap.getOrDefault(sheetName, null);
}
/**
* 通过Model类,读取工作表数据
*
* @param file 文件
* @param sheetName sheetName
* @param clazz BaseRowModel
* @return 数据集合
*/
public static List<? extends BaseRowModel> readExcelByModelSheetName(File file, String sheetName,
Class<? extends BaseRowModel> clazz) {
Map<String, List<? extends BaseRowModel>> dataListMap = readExcelByModel(file, sheetName, clazz);
return dataListMap.getOrDefault(sheetName, null);
}
/**
* 通过Model类,读取工作表数据
*
* @param filePath 文件路径
* @param sheetName sheetName
* @param clazz BaseRowModel
* @return 数据集合
*/
public static Map<String, List<? extends BaseRowModel>> readExcelByModel(String filePath, String sheetName,
Class<? extends BaseRowModel> clazz) {
Map<String, List<? extends BaseRowModel>> dataListMap;
InputStream inputStream = null;
try {
// 创建文件流
inputStream = new FileInputStream(filePath);
dataListMap = readExcelByModelFromInputStream(inputStream, sheetName, clazz);
} catch (Exception e) {
throw new EasyExcelException("readExcelByModel from filePath failed." + e, e);
} finally {
// 关闭文件流
try {
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
LOGGER.error("inputStream.close failed!", e);
}
}
return dataListMap;
}
/**
* 通过Model类,读取工作表数据
*
* @param file 文件
* @param sheetName sheetName
* @param clazz BaseRowModel
* @return 数据集合
*/
public static Map<String, List<? extends BaseRowModel>> readExcelByModel(File file, String sheetName,
Class<? extends BaseRowModel> clazz) {
Map<String, List<? extends BaseRowModel>> dataListMap;
InputStream inputStream = null;
try {
// 创建文件流
inputStream = new FileInputStream(file);
dataListMap = readExcelByModelFromInputStream(inputStream, sheetName, clazz);
// 关闭文件流
inputStream.close();
} catch (Exception e) {
throw new EasyExcelException("readExcelByModel from File failed." + e, e);
} finally {
// 关闭文件流
try {
if (null != inputStream) {
inputStream.close();
}
} catch (IOException e) {
LOGGER.error("inputStream.close failed!", e);
}
}
return dataListMap;
}
/**
* 通过Model类,读取工作表数据
*
* @param inputStream 文件流
* @param sheetName sheetName
* @param clazz BaseRowModel
* @return 数据集合
*/
public static Map<String, List<? extends BaseRowModel>> readExcelByModelFromInputStream(InputStream inputStream,
String sheetName,
Class<?
extends BaseRowModel> clazz) {
// 解析每行结果在listener中处理
// 创建返回信息
Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap();
// 解析监听器
ModelExcelListener excelListener = new ModelExcelListener();
try {
// 创建文件流
ExcelReader excelReader = EasyExcelFactory.getReader(inputStream, excelListener);
// 得到所有工作表
List<Sheet> sheets = excelReader.getSheets();
// 取所有工作表数据
for (Sheet sheet : sheets) {
// 工作表名称
String currentSheetName = sheet.getSheetName();
if (Strings.isNullOrEmpty(sheetName) || Splitter.on(',').trimResults().omitEmptyStrings().splitToList(
sheetName).contains(currentSheetName)) {
// 设置模板
sheet.setClazz(clazz);
// 读取Excel数据
excelReader.read(sheet);
// 返回明细数据
List<? extends BaseRowModel> sheetDataInfo = Lists.newArrayList(excelListener.getDataList());
// 将工作表数据放入工作薄
dataListMap.put(currentSheetName, sheetDataInfo);
// 清除缓存数据
excelListener.clear();
}
}
} catch (Exception e) {
throw new EasyExcelException("readExcelByModel from inputStream failed." + e, e);
}
return dataListMap;
}
/**
* 通过String类,将一个sheet写入到一个Excel
*
* @param filePath 文件路径
* @param sheetName sheetName
* @param dataList 数据集
*/
public static void writeExcelByString(String filePath, String sheetName, List<List<String>> dataList) {
// 创建返回信息
Map<String, List<List<String>>> dataListMap = Maps.newLinkedHashMap();
// 将工作表放入到Excel中
dataListMap.put(sheetName, dataList);
// 输出Excel数据
writeExcelByString(filePath, dataListMap);
}
/**
* 通过String类,将多个sheet写入到一个Excel
*
* @param filePath 文件路径
* @param dataListMap 数据集
*/
public static void writeExcelByString(String filePath, Map<String, List<List<String>>> dataListMap) {
try {
// 工作表编号
int sheetNo = 1;
// 创建文件流
OutputStream out = new FileOutputStream(filePath);
ExcelWriter writer = EasyExcelFactory.getWriter(out);
// 循环写入每个工作表
for (Entry<String, List<List<String>>> entry : dataListMap.entrySet()) {
// 得到工作表名称
String sheetName = entry.getKey();
// 得到工作表数据
List<List<String>> dataList = entry.getValue();
// 设置工作表信息
Sheet sheet1 = new Sheet(sheetNo++, 1, null, sheetName, null);
// 设置开始行为-1
sheet1.setStartRow(-1);
// 设置自适应宽度
sheet1.setAutoWidth(Boolean.TRUE);
// 开始写数据
writer.write0(dataList, sheet1);
}
// 清空缓存
writer.finish();
// 关闭文件
out.close();
} catch (Exception e) {
throw new EasyExcelException("writeExcelByString failed." + e, e);
}
}
/**
* 通过Model类,将一个sheet写入到一个Excel
*
* @param filePath 文件路径
* @param sheetName sheetName
* @param dataList 数据集
* @param clazz BaseRowModel
*/
public static void writeExcelByModel(String filePath, String sheetName, List<? extends BaseRowModel> dataList,
Class<? extends BaseRowModel> clazz) {
// 创建返回信息
Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap();
// 将工作表放入到Excel中
dataListMap.put(sheetName, dataList);
// 输出Excel数据
writeExcelByModel(filePath, dataListMap, clazz);
}
/**
* 通过String类,将多个sheet写入到一个Excel
*
* @param filePath 文件路径
* @param dataListMap 数据集
* @param clazz BaseRowModel
*/
public static void writeExcelByModel(String filePath, Map<String, List<? extends BaseRowModel>> dataListMap,
Class<? extends BaseRowModel> clazz) {
try {
// 创建文件流
OutputStream out = new FileOutputStream(filePath);
// 写入文件
writeIntoOutputStream(dataListMap, clazz, out);
// 关闭文件
out.close();
} catch (Throwable e) {
throw new EasyExcelException("write to file failed." + e, e);
}
}
/**
* export to byte array.
*
* @param sheetName sheetName
* @param dataList data
* @param clazz BaseRowModel
* @return return a byte array with data.
*/
public static byte[] exportByteArray(String sheetName, List<? extends BaseRowModel> dataList,
Class<? extends BaseRowModel> clazz) {
// 创建返回信息
Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap();
// 将工作表放入到Excel中
dataListMap.put(sheetName, dataList);
ByteArrayOutputStream out = new ByteArrayOutputStream();
writeIntoOutputStream(dataListMap, clazz, out);
return out.toByteArray();
}
/**
* export to byte array.
*
* @param dataListMap data
* @param clazz BaseRowModel
* @return return a byte array with data.
*/
public static byte[] exportByteArray(Map<String, List<? extends BaseRowModel>> dataListMap,
Class<? extends BaseRowModel> clazz) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
writeIntoOutputStream(dataListMap, clazz, out);
return out.toByteArray();
}
/**
* 数据写入输出流
*
* @param dataListMap 数据
* @param clazz BaseRowModel
* @param outputStream 输出流
*/
public static void writeIntoOutputStream(Map<String, List<? extends BaseRowModel>> dataListMap,
Class<? extends BaseRowModel> clazz,
OutputStream outputStream) {
try {
// 工作表编号
int sheetNo = 1;
// 创建文件流
ExcelWriter writer = EasyExcelFactory.getWriter(outputStream);
// 循环写入每个工作表
for (Entry<String, List<? extends BaseRowModel>> entry : dataListMap.entrySet()) {
// 得到工作表名称
String sheetName = entry.getKey();
// 得到工作表数据
List<? extends BaseRowModel> dataList = entry.getValue();
// 设置工作表信息
Sheet sheet1 = new Sheet(sheetNo++, 1, clazz, sheetName, null);
// 设置自适应宽度
sheet1.setAutoWidth(Boolean.TRUE);
// 开始写数据
writer.write(dataList, sheet1);
}
// 清空缓存
writer.finish();
} catch (Throwable e) {
throw new EasyExcelException("write to OutputStream failed." + e, e);
}
}
/**
* String类,解析监听器
*/
private static class StringExcelListener extends AnalysisEventListener<List<String>> {
/**
* 自定义用于暂时存储data 可以通过实例获取该值
*/
private List<List<String>> dataList = Lists.newArrayList();
@Override
public void invoke(List<String> rowInfo, AnalysisContext context) {
// 数据存储到list,供批量处理,或后续自己业务逻辑处理。
dataList.add(rowInfo);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//解析结束销毁不用的资源
}
private List<List<String>> getDataList() {
return dataList;
}
private void setDataList(List<List<String>> dataList) {
this.dataList = dataList;
}
private void clear() {
dataList.clear();
}
}
/**
* Model类,解析监听器
*/
private static class ModelExcelListener extends AnalysisEventListener<BaseRowModel> {
/**
* 自定义用于暂时存储data 可以通过实例获取该值
*/
private List<BaseRowModel> dataList = Lists.newArrayList();
@Override
public void invoke(BaseRowModel rowInfo, AnalysisContext context) {
dataList.add(rowInfo);
}
/**
* 解析结束销毁不用的资源
*
* @param context AnalysisContext
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//解析结束销毁不用的资源
}
/**
* 获取
*
* @return 返回sheet数据
*/
private List<? extends BaseRowModel> getDataList() {
return dataList;
}
/**
* 设置sheet数据
*
* @param dataList 数据
*/
private void setDataList(List<BaseRowModel> dataList) {
this.dataList = dataList;
}
/**
* 清空数据
*/
private void clear() {
dataList.clear();
}
}
/**
* EasyExcelException
*/
public static class EasyExcelException extends RuntimeException {
private static final long serialVersionUID = -5456062088984840434L;
public EasyExcelException() {
super();
}
public EasyExcelException(String message) {
super(message);
}
public EasyExcelException(String message, Throwable cause) {
super(message, cause);
}
public EasyExcelException(Throwable cause) {
super(cause);
}
}
}
样例:
/**
* Excel模型
*
* @author zhao.yingchao
* @date 2019年02月27日
* @since v1.0.0
*/
@Data
public class Test extends BaseRowModel {
@ExcelProperty(value = "姓名", index = 0)
private String name;
@ExcelProperty(value = "年龄", index = 1)
private String age;
@ExcelProperty(value = "手机号", index = 2)
private String phoneNum;
}
使用:
/**
* @author zhao.yingchao
* @date 2019-02-27
* @since v1.0.0
*/
public class EasyExcelUtilTest {
@Test
public void test_parse_excel() {
Resource resource = new ClassPathResource("file/list1W.xlsx");
try {
long startTime = MonitorUtil.startLogInfo("parse begin");
List<Test> list = (List<Test>)EasyExcelUtil.readExcelByModelSheetName(resource.getFile(),
"Sheet1",
Test.class);
MonitorUtil.endLogInfo(startTime, "parse end");
Assert.assertEquals(list.size(), 10000);
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用问题请与我反馈,谢谢
正因为当初对未来做了太多的憧憬,所以对现在的自己尤其失望。生命中曾经有过的所有灿烂,终究都需要用寂寞来偿还。