目录
1. 概述
官网地址:关于Easyexcel | Easy Excel (alibaba.com)
EasyExcel 是一个基于 Java 的、快速、简洁、解决大文件内存溢出的 Excel 处理工具。他能让你在不用考虑性能、内存的等因素的情况下,快速完成 Excel 的读、写等功能。
导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>最新版本</version>
</dependency>
2. 读excel表信息
2.1 简单读取-创建实体类
可以使用与excel表头对应的实体类来读取,以下有两种注解可以自由选取。
第一种就是使用@Excelproperty中的value值去读取表数据,不过要注意不能出现重复字段。
@Data
public class DemoData {
/**
* 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据。
* @ExcelProperty注解的value值是一个数组类型,如果时如图所示这种有多级标题的可以这么写。
*/
@ExcelProperty({"xxx有限公司","序号"})
private Integer id;
@ExcelProperty({"xxx有限公司","姓名"})
private String name;
@ExcelProperty({"xxx有限公司","年龄"})
private Integer age;
}
第二种是采用@ExcelProperty中的index字段,根据表格列号去读数据,index值是从0开始,一般不建议index和value同时使用。
@Data
public class DemoData {
@ExcelProperty(index = 0)
private Integer id;
@ExcelProperty(index = 1)
private String name;
@ExcelProperty(index = 2)
private Integer age;
}
2.2 日期、数字或者自定义格式转换
@Getter
@Setter
@EqualsAndHashCode
public class ConverterData {
/**
* 我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:”
*/
@ExcelProperty(converter = CustomStringStringConverter.class)
private String string;
/**
* 这里用string 去接日期才能格式化。我想接收年月日格式
*/
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
private String date;
/**
* 我想接收百分比的数字
*/
@NumberFormat("#.##%")
private String doubleData;
}
2.3 监听器
public class DemoDataExcelListener implements ReadListener<VerticalCurveElementExcelData> {
// 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
private static final int BATCH_COUNT = 100;
private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
private List<DemoDataEntity> demoDatatEntityList = new ArrayList();
private String fileName;
public void setFileName(String fileName) {
this.fileName = fileName;
}
public DemoDataExcelListener() {
}
public List<DemoDataEntity> getDatas() {
return this.demoDataEntityList;
}
/**
* 在监听器出现异常时会调用此方法
*/
public void onException(Exception exception, AnalysisContext context) throws Exception {
if (exception.getClass() == ExcelAnalysisStopException.class) {
System.out.println("解析失败" + exception.getMessage());
throw exception;
} else {
System.out.println("解析失败,但是继续解析下一行:" + exception.getMessage());
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
System.out.println("第" + excelDataConvertException.getRowIndex() + "行,第" + excelDataConvertException.getColumnIndex() + "列解析异常,数据为:" + excelDataConvertException.getCellData());
}
}
}
/**
* 每一条头数据解析都会来调用
*/
public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
System.out.println("解析到一条头数据:" + JSON.toJSONString(headMap));
if (headMap.size() < "你要读取的列数") {
throw new ExcelAnalysisStopException("读取excel模板不匹配");
}
}
/**
* 这个每一条数据解析都会来调用
*/
@Override
public void invoke(DemoDataExcelData data, AnalysisContext context) {
this.cachedDataList.add(data);
if (this.cachedDataList.size() >= BATCH_COUNT) {
this.transData();
this.cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
this.transData();
}
private void transData() {
// ...数据转换的内容
}
}
2.4 监听器调用
public static List<DemoDataEntity> readDemoDataData(InputStream inputStream, String fileName) {
Object listMap = new ArrayList();
try {
DemoDataExcelListener listener = new DemoDataExcelListener();
listener.setFileName(fileName);
ExcelReader excelReader = EasyExcelFactory.read(inputStream, DemoDataExcelData.class, listener).build();
excelReader.readAll();
listMap = listener.getDatas();
excelReader.finish();
} catch (Exception var5) {
var5.printStackTrace();
}
return (List)listMap;
}
2.5 向数据库内存入数据
@Override
public List<DemoDataEntity> demoDataExcelRead(@RequestParam("file") MultipartFile file, Integer projectId) throws IOException {
List<DemoDataEntity> entitys = EeasyExcelUtil.readDemoDataData(file.getInputStream(),file.getOriginalFilename());
// 储存数据
savaExcelList(entitys,projectId);
return entitys;
}
3. 写excel
3.1 DemoData
这里有模板的话,可以不使用@ExcelProperty。
@Data
public class DemoData {
private Integer id;
private String name;
private Integer age;
}
3.2 填充excel
这里做完excel填充之后,继续将excel表格上传至minIO,如果想详细了解,可参考MinIO的上传和下载
如果需要直接填充完excel响应给页面的话,可以使用EasyExcel.write(response.getOutputStream)
public void DemoDataUploadMinIo(Object ...) throws IOException {
// 获取从数据库中查询到并经过处理后可以填充至excel中的DemoData数据,如果要填充多个sheet的话,可以使用Map<String, List<DemoData>>
Map<String, List<DemoData>> demoDataExcelData = toDemoData(param);
// 判断是否获取到数据
boolean flag = false;
for (String s : demoDataExcelData.keySet()) {
if (demoDataExcelData.get(s) != null && demoDataExcelData.get(s).size() > 0) {
flag = true;
break;
}
}
if (flag) {
// 获取模板路径
String dataPath= excelTemplateRealpath + "员工信息表.xlsx";
InputStream is = new FileInputStream(dataPath);
is = copySheet(routes, is);
// 读取模板表
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
// 由于这里获取到数据做的是上传,所以不用response.getOutputStream来获取响应流
ExcelWriter excelWriter = EasyExcel.write(outputStream).excelType(ExcelTypeEnum.XLSX).withTemplate(is).build();
// 遍历多个数据,进行填充
for (String str: demoDataExcelData.keySet()) {
// 获取不同路线的值
List<DemoData> value = demoDataExcelData.get(str);
WriteSheet writeSheet = EasyExcel.writerSheet("sheet名称").build();
// 使用fill()方法,必须要有模板,不然会报错
excelWriter.fill(value, writeSheet);
Map<String, Object> map = MapUtils.newHashMap();
map.put("company", "EasyExcel");
map.put("date", "2023-07-14");
excelWriter.fill(map, writeSheet);
}
excelWriter.finish();
byte[] bytes = outputStream.toByteArray();
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
MultipartFile multipartFile = new MockMultipartFile("直线、曲线及转角表" + projectName + ".xlsx", inputStream);
// 上传文件至minIo并储存文件信息至db
FileService.insertFile(multipartFile, ...);
outputStream.close();
is.close();
}
}
/**
* 复制sheet,这里是采用POI来复制sheet
*/
public InputStream copySheet(List<KProjectRouteEntity> routes, InputStream is) throws IOException {
XSSFWorkbook sheets = new XSSFWorkbook(is);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for (int i = 0; i < routes.size(); i++) {
String sheetName = routes.get(i).getRouteName();
if (i == 0) {
sheets.setSheetName(i, sheetName);
} else {
sheets.cloneSheet(0, sheetName);
}
}
sheets.write(bos);
byte[] bArray = bos.toByteArray();
// 不需要调用close()关闭流,在调用ExcelWriter.finish()会被关闭
return new ByteArrayInputStream(bArray);
}