参考资料:
easyexcel官网:https://easyexcel.opensource.alibaba.com/
什么是alibaba-easyexcel
EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
其他的Excel处理工具:
Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等
alibaba-easyexcel与其它框架的区别:
Apache poi、jxl等处理Excel的框架,他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。而EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。
pom依赖
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.7</version> </dependency>
|
1、创建demo
2、导入pom依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| <dependencies>
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.1.7</version> </dependency>
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.5</version> </dependency>
<dependency> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> <version>3.1.0</version> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
</dependencies>
|
2、创建pojo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import com.alibaba.excel.annotation.ExcelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;
@Data @NoArgsConstructor @AllArgsConstructor public class ExcelOrder {
@ExcelProperty("订单编号") private String orderId;
@ExcelProperty("商品名称") private String tradeName;
@ExcelProperty("成本价") private Double costPrice;
@ExcelProperty("销售价") private Double sellingPrice; }
|
写入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import com.alibaba.excel.EasyExcel; import org.junit.Test;
import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List;
public class ExcelWriteTest {
@Test public void excelWrite(){ File excelFile = new File("./订单表.xlsx"); if (!excelFile.exists()) { try { excelFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } EasyExcel.write(excelFile, ExcelOrder.class).sheet("订单模版").doWrite(data()); }
private List<ExcelOrder> data(){ List excelOrderList = new ArrayList<>();
for (int i=0;i<65535;i++){ ExcelOrder data = new ExcelOrder(); data.setOrderId("20220224"+(i+1)); data.setTradeName("商品名称"+i); data.setCostPrice(i+5.0); data.setSellingPrice(i+10.0); excelOrderList.add(data); }
return excelOrderList; } }
|
结果展示:
读取数据
1)、创建监听器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import lombok.extern.slf4j.Slf4j;
@Slf4j public class EasyExcelOrderListener extends AnalysisEventListener<ExcelOrder> {
@Override public void invoke(ExcelOrder data, AnalysisContext context) { log.info("解析到一条数据:"+data); }
@Override public void doAfterAllAnalysed(AnalysisContext context) { log.info("所有数据解析完成!!!"); } }
|
2)、读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import com.alibaba.excel.EasyExcel; import org.junit.Test;
public class ExcelReadTest {
@Test public void excelRead(){ String fileName = "./订单表.xlsx"; EasyExcel.read(fileName, ExcelOrder.class,new EasyExcelOrderListener()).sheet().doRead(); } }
|
扩展阅读:
100000 行级别数据的 Excel 导入优化之路
读取数据-Demo
上传方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Override public void uploadBatch(CaseExcelReq req, String key) { String wxCorpId = SecurityUser.getWxCorpId(); Long userId = SecurityUser.getUserId(); String excelUrl = req.getExcelUrl(); log.info("开始解析excel: uploadBatch -> wxCorpId = {}, key = {}, excelUrl = {}", wxCorpId, key, excelUrl); if (!(excelUrl.endsWith(ExcelTypeEnum.XLSX.getValue()) || excelUrl.endsWith(ExcelTypeEnum.XLS.getValue()))) { log.info("uploadBatch -> excel 格式有误!"); return; }
Long logId = jsonStoreService.generatorStoreId(); CaseExcelUploadLogEntity entity = new CaseExcelUploadLogEntity(); entity.setId(logId); entity.setFileName(req.getFileName()); entity.setExcelUrl(excelUrl); entity.setStatus(1); entity.setCreator(userId); entity.setCreatorName(sysUserService.get(userId).getRealName()); entity.setCreateDate(DateUtil.date()); caseExcelUploadLogService.save(entity); log.info("uploadBatch: 存储成功解析中的状态实体 entity -> {}", entity);
doExcelParse(excelUrl, wxCorpId, userId, logId, key);
}
|
异步调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @Override public void doExcelParse(String excelUrl, String wxCorpId, Long userId, Long logId, String key) { log.info("开始异步解析表格: traceId -> {}", key); CompletableFuture<Void> future = CompletableAsyncUtil.run(30, TimeUnit.MINUTES, () -> { TraceIdGenerator.setTraceId(key);
InputStream inputStream = null; try { inputStream = sysOssService.download(excelUrl); if (Objects.isNull(inputStream)) { throw new RenException("未读取到对应的文件信息"); } EasyExcel.read(inputStream, CaseExcelFeildDTO.class, new CaseBaseInfoDataListener(this, excelUrl, excelImportLimitCount, userId, wxCorpId, logId)) .sheet() .headRowNumber(2) .doRead();
TraceIdGenerator.removeTraceId(); } catch (Exception e) { log.error("uploadBatch 上传excel解析失败:error -> {}", e.getMessage(), e); } finally { if (Objects.nonNull(inputStream)) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }); }
|
解析类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.exception.ExcelAnalysisException; import com.alibaba.fastjson.JSON; import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; import java.util.List;
@Slf4j public class CaseBaseInfoDataListener extends AnalysisEventListener<CaseExcelFeildDTO> { private List<CaseExcelFeildDTO> cachedDataList; private CaseBaseInfoService caseBaseInfoService; private String excelUrl; private Integer limitCount; private String wxCorpId; private Long userId; private Long logId;
public CaseBaseInfoDataListener(CaseBaseInfoService service, String excelUrl, Integer limit, Long userId, String wxCorpId, Long logId) { log.info("CaseBaseInfoDataListener init: service={}, limit={}, wxCorpId={}", service, limit, wxCorpId); this.caseBaseInfoService = service; this.excelUrl = excelUrl; this.limitCount = limit; this.wxCorpId = wxCorpId; this.userId = userId; this.logId = logId; cachedDataList = Lists.newArrayListWithCapacity(this.limitCount + 1); }
@Override public void invoke(CaseExcelFeildDTO data, AnalysisContext analysisContext) { log.info("invoke() 解析到一条数据:{}", JSON.toJSONString(data)); cachedDataList.add(data); if (cachedDataList.size() > limitCount) { String msg = "最大支持导入的条数为" + limitCount + "条!"; caseBaseInfoService.updateFailLog(Lists.newArrayList(msg), logId);
log.warn(msg);
throw new ExcelAnalysisException(msg); } }
@Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { log.info("所有数据【解析】完成!"); this.saveData(); log.info("所有数据【保存】完成!"); }
private void saveData() { boolean res = caseBaseInfoService.saveBatchFromExcel(cachedDataList, excelUrl, userId, wxCorpId, logId); log.info(res ? "存储数据库成功!" : "存储数据库失败!"); } }
|