前言:
easyExcel 的官网文档给的示例非常全,可以参考https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read
在此我贴出自己的工具类,可以直接用
导包
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.0</version></dependency>
读excel
1:最简单读-使用demo对象
创建对象bean
package com.wkl.testdemo.excel;import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;/*** @author wangkanglu* @version 1.0* @description* @date 2023-12-08 17:28*/
@Data
public class DemoBean {/*** 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据*/@ExcelProperty("姓名")private String name;/*** 强制读取第二个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配*/@ExcelProperty(index = 1)private String sex;
}
创建读取器
package com.wkl.testdemo.excel;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.util.ListUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;import java.util.List;/*** @author wangkanglu* @version 1.0* @description* @date 2023-12-08 17:27*/
@Data
public class DemoListener extends AnalysisEventListener<DemoBean> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;/*** 缓存的数据*/private List<DemoBean> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);/*** 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。*/private DemoBean demoDAO;public DemoListener() {// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数demoDAO = new DemoBean();}@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {if (exception instanceof ExcelDataConvertException) {Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;String message = "第" + rowIndex + "行,第" + columnIndex + "列,数据格式有误,请核实";System.out.println("导入数据转换出现错误: " + message);} else if (exception instanceof RuntimeException) {System.out.println("导入错误:" + exception);if (exception.getMessage().contains("列与模板上顺序不符,请勿修改表头模板")) {throw new RuntimeException(exception.getMessage());}}}@Overridepublic void invoke(DemoBean data, AnalysisContext analysisContext) {System.out.println("解析到一条数据:{}"+data);cachedDataList.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (cachedDataList.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listcachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();System.out.println("解析完了");}/*** 加上存储数据库*/private void saveData() {System.out.println("{}条数据,开始存储数据库!"+ cachedDataList.size());
// demoDAO.save(cachedDataList);System.out.println("存储数据库成功!");}}
测试程序
package com.wkl.testdemo.excel;import com.alibaba.excel.EasyExcel;import java.util.List;/*** @author wangkanglu* @version 1.0* @description* @date 2023-12-08 17:45*/
public class SimpleTest {public static void main(String[] args) {String path = "C:\\Users\\Desktop\\demo.xlsx";DemoListener listener = new DemoListener();EasyExcel.read(path,DemoBean.class,listener).sheet().doRead();List<DemoBean> cachedDataList = listener.getCachedDataList();System.out.println("end----");}
}
2:读取多个sheet
public static void main(String[] args) {String path = "C:\\Users\\wenge\\Desktop\\demo.xlsx";DemoListener listener = new DemoListener();//生成读取对象ExcelReader excelReader = EasyExcel.read(path).build();//生成一个sheetReadSheet build = EasyExcel.readSheet(0).head(DemoBean.class).registerReadListener(listener).build();//读取多个sheetExcelReader read = excelReader.read(Arrays.asList(build));List<DemoBean> cachedDataList = listener.getCachedDataList();System.out.println("end----");
3:复杂头的读取
如果遇到这样的excel
创建对象bean
package com.wkl.testdemo.excel;import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;/*** @author wangkanglu* @version 1.0* @description* @date 2023-12-08 17:28*/
@Data
public class DemoBeanheads {/*** 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据*/@ExcelProperty({"大学","姓名"})private String name;/*** 强制读取第二个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配*/@ExcelProperty({"大学","性别"})private String sex;
}
读取器不变
测试程序
public static void main(String[] args) {String path = "C:\\Users\\Desktop\\demo.xlsx";DemoHeadListener listener = new DemoHeadListener();//生成读取对象ExcelReader excelReader = EasyExcel.read(path).build();//生成一个sheetReadSheet build = EasyExcel.readSheet(0).head(DemoBeanheads.class).registerReadListener(listener).headRowNumber(2).build();//读取多个sheetExcelReader read = excelReader.read(Arrays.asList(build));List<DemoBeanheads> cachedDataList = listener.getCachedDataList();System.out.println("end----");}
4:不创建对象的对
读取器
package com.wkl.testdemo.excel;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelDataConvertException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ObjectUtils;import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;@Slf4j
public class NoHeadMapListener extends AnalysisEventListener<Map<Integer, String>> {private List<Map<Integer, String>> list = new ArrayList<>();private static final SimpleDateFormat sdfFormat = new SimpleDateFormat("yyyy-MM-dd");//表头private List<String> headList = Arrays.asList("姓名","性别");@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {if (headMap==null){throw new RuntimeException("文件表头为空,请勿上传非法excel文件,请先下载对应的导入模板文件" );}if (headMap.size()!=headList.size()){throw new RuntimeException("文件表头列与模板不符,请勿上传非法excel文件,请先下载对应的导入模板文件" );}for (int i = 0; i < headList.size(); i++) {String tableHead = headMap.get(i);String head = headList.get(i);if (!head.equals(tableHead)) {throw new RuntimeException("表头中'" + tableHead + "'列与模板上顺序不符,请勿修改模板表头");}}}@Overridepublic void invoke(Map<Integer, String> integerStringMap, AnalysisContext analysisContext) {
// log.info("解析到一条数据:{}", JSON.toJSONString(integerStringMap));List<String> picList = new ArrayList<>();if (!ObjectUtils.isEmpty(integerStringMap)) {list.add(integerStringMap);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {log.info("所有数据解析完成!");}@Overridepublic void onException(Exception exception, AnalysisContext context) throws Exception {if (exception instanceof ExcelDataConvertException) {Integer columnIndex = ((ExcelDataConvertException) exception).getColumnIndex() + 1;Integer rowIndex = ((ExcelDataConvertException) exception).getRowIndex() + 1;String message = "第" + rowIndex + "行,第" + columnIndex + "列,数据格式有误,请核实";log.error("导入数据转换出现错误: " + message);} else if (exception instanceof RuntimeException) {log.error("导入错误:" + exception);if (exception.getMessage().contains("列与模板上顺序不符,请勿修改表头模板")) {throw new RuntimeException(exception.getMessage());}}}public List<String> getHeadList() {return headList;}public void setHeadList(List<String> headList) {this.headList = headList;}public List<Map<Integer, String>> getList() {return list;}}
测试程序
public static void main(String[] args) {String path = "C:\\Users\\Desktop\\demo.xlsx";NoHeadMapListener listener = new NoHeadMapListener();EasyExcel.read(path, listener).sheet().doRead();List<String> headList = listener.getHeadList();List<Map<Integer, String>> list = listener.getList();System.out.println("end");}
读取后的得到的数据列表:
写excel
1:最简单的写
创建对象
@Getter
@Setter
@EqualsAndHashCode
public class DemoData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;/*** 忽略这个字段*/@ExcelIgnoreprivate String ignore;
}
测试程序
List<DemoData> data = new ArrayList();
fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data);
测试程序-导出指定列
List<DemoData> data = new ArrayList();
fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";// 根据用户传入字段 假设我们要忽略 name列Set<String> excludeColumnFiledNames = new HashSet<String>();excludeColumnFiledNames.add("name");// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板").doWrite(data);// 根据用户传入字段 假设我们只要导出 name列Set<String> includeColumnFiledNames = new HashSet<String>();includeColumnFiledNames.add("name");// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(fileName, DemoData.class).includeColumnFiledNames(includeColumnFiledNames).sheet("模板").doWrite(data);
2:数据写到不同的sheet
fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";// 这里 指定文件try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面for (int i = 0; i < 5; i++) {// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<DemoData> data = data();excelWriter.write(data, writeSheet);}}
3:动态表头写入web
private List<List<String>> head() {List<List<String>> list = new ArrayList<List<String>>();List<String> head0 = new ArrayList<String>();head0.add("字符串" + System.currentTimeMillis());List<String> head1 = new ArrayList<String>();head1.add("数字" + System.currentTimeMillis());List<String> head2 = new ArrayList<String>();head2.add("日期" + System.currentTimeMillis());list.add(head0);list.add(head1);list.add(head2);return list;}private List<DemoData> data() {List<DemoData> list = ListUtils.newArrayList();for (int i = 0; i < 10; i++) {DemoData data = new DemoData();data.setString("字符串" + i);data.setDate(new Date());data.setDoubleData(0.56);list.add(data);}return list;}@GetMapping("download")public void download(HttpServletResponse response) throws IOException {// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postmanresponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());}