示例代码

DEMO代码地址:https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/read/ReadTest.java

最简单的读

excel示例

读 - 图1

对象
  1. @Data
  2. public class DemoData {
  3. private String string;
  4. private Date date;
  5. private Double doubleData;
  6. }

监听器
  1. // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
  2. public class DemoDataListener extends AnalysisEventListener<DemoData> {
  3. private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
  4. /**
  5. * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
  6. */
  7. private static final int BATCH_COUNT = 5;
  8. List<DemoData> list = new ArrayList<DemoData>();
  9. /**
  10. * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
  11. */
  12. private DemoDAO demoDAO;
  13. public DemoDataListener() {
  14. // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
  15. demoDAO = new DemoDAO();
  16. }
  17. /**
  18. * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
  19. *
  20. * @param demoDAO
  21. */
  22. public DemoDataListener(DemoDAO demoDAO) {
  23. this.demoDAO = demoDAO;
  24. }
  25. /**
  26. * 这个每一条数据解析都会来调用
  27. *
  28. * @param data
  29. * one row value. Is is same as {@link AnalysisContext#readRowHolder()}
  30. * @param context
  31. */
  32. @Override
  33. public void invoke(DemoData data, AnalysisContext context) {
  34. LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
  35. list.add(data);
  36. // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
  37. if (list.size() >= BATCH_COUNT) {
  38. saveData();
  39. // 存储完成清理 list
  40. list.clear();
  41. }
  42. }
  43. /**
  44. * 所有数据解析完成了 都会来调用
  45. *
  46. * @param context
  47. */
  48. @Override
  49. public void doAfterAllAnalysed(AnalysisContext context) {
  50. // 这里也要保存数据,确保最后遗留的数据也存储到数据库
  51. saveData();
  52. LOGGER.info("所有数据解析完成!");
  53. }
  54. /**
  55. * 加上存储数据库
  56. */
  57. private void saveData() {
  58. LOGGER.info("{}条数据,开始存储数据库!", list.size());
  59. demoDAO.save(list);
  60. LOGGER.info("存储数据库成功!");
  61. }
  62. }

持久层
  1. /**
  2. * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
  3. **/
  4. public class DemoDAO {
  5. public void save(List<DemoData> list) {
  6. // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
  7. }
  8. }

代码
  1. /**
  2. * 最简单的读
  3. * <p>1. 创建excel对应的实体对象 参照{@link DemoData}
  4. * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
  5. * <p>3. 直接读即可
  6. */
  7. @Test
  8. public void simpleRead() {
  9. // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
  10. // 写法1:
  11. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  12. // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
  13. EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
  14. // 写法2:
  15. fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  16. ExcelReader excelReader = null;
  17. try {
  18. excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
  19. ReadSheet readSheet = EasyExcel.readSheet(0).build();
  20. excelReader.read(readSheet);
  21. } finally {
  22. if (excelReader != null) {
  23. // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
  24. excelReader.finish();
  25. }
  26. }
  27. }

指定列的下标或者列名

excel示例

参照:excel示例

对象
  1. @Data
  2. public class IndexOrNameData {
  3. /**
  4. * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
  5. */
  6. @ExcelProperty(index = 2)
  7. private Double doubleData;
  8. /**
  9. * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
  10. */
  11. @ExcelProperty("字符串标题")
  12. private String string;
  13. @ExcelProperty("日期标题")
  14. private Date date;
  15. }

监听器

参照:监听器 只是泛型变了而已

代码
  1. /**
  2. * 指定列的下标或者列名
  3. *
  4. * <p>1. 创建excel对应的实体对象,并使用{@link ExcelProperty}注解. 参照{@link IndexOrNameData}
  5. * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link IndexOrNameDataListener}
  6. * <p>3. 直接读即可
  7. */
  8. @Test
  9. public void indexOrNameRead() {
  10. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  11. // 这里默认读取第一个sheet
  12. EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();
  13. }

读多个sheet

excel示例

参照:excel示例

对象

参照:对象

监听器

参照:监听器

代码
  1. /**
  2. * 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件
  3. * <p>
  4. * 1. 创建excel对应的实体对象 参照{@link DemoData}
  5. * <p>
  6. * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
  7. * <p>
  8. * 3. 直接读即可
  9. */
  10. @Test
  11. public void repeatedRead() {
  12. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  13. // 读取全部sheet
  14. // 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写
  15. EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
  16. // 读取部分sheet
  17. fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  18. ExcelReader excelReader = null;
  19. try {
  20. excelReader = EasyExcel.read(fileName).build();
  21. // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener
  22. ReadSheet readSheet1 =
  23. EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
  24. ReadSheet readSheet2 =
  25. EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
  26. // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
  27. excelReader.read(readSheet1, readSheet2);
  28. } finally {
  29. if (excelReader != null) {
  30. // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
  31. excelReader.finish();
  32. }
  33. }
  34. }

日期、数字或者自定义格式转换

excel示例

参照:excel示例

对象
  1. @Data
  2. public class ConverterData {
  3. /**
  4. * 我自定义 转换器,不管数据库传过来什么 。我给他加上“自定义:”
  5. */
  6. @ExcelProperty(converter = CustomStringStringConverter.class)
  7. private String string;
  8. /**
  9. * 这里用string 去接日期才能格式化。我想接收年月日格式
  10. */
  11. @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
  12. private String date;
  13. /**
  14. * 我想接收百分比的数字
  15. */
  16. @NumberFormat("#.##%")
  17. private String doubleData;
  18. }

监听器

参照:监听器 只是泛型变了

自定义转换器
  1. public class CustomStringStringConverter implements Converter<String> {
  2. @Override
  3. public Class supportJavaTypeKey() {
  4. return String.class;
  5. }
  6. @Override
  7. public CellDataTypeEnum supportExcelTypeKey() {
  8. return CellDataTypeEnum.STRING;
  9. }
  10. /**
  11. * 这里读的时候会调用
  12. *
  13. * @param cellData
  14. * NotNull
  15. * @param contentProperty
  16. * Nullable
  17. * @param globalConfiguration
  18. * NotNull
  19. * @return
  20. */
  21. @Override
  22. public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
  23. GlobalConfiguration globalConfiguration) {
  24. return "自定义:" + cellData.getStringValue();
  25. }
  26. /**
  27. * 这里是写的时候会调用 不用管
  28. *
  29. * @param value
  30. * NotNull
  31. * @param contentProperty
  32. * Nullable
  33. * @param globalConfiguration
  34. * NotNull
  35. * @return
  36. */
  37. @Override
  38. public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,
  39. GlobalConfiguration globalConfiguration) {
  40. return new CellData(value);
  41. }
  42. }

代码
  1. /**
  2. * 日期、数字或者自定义格式转换
  3. * <p>
  4. * 默认读的转换器{@link DefaultConverterLoader#loadDefaultReadConverter()}
  5. * <p>1. 创建excel对应的实体对象 参照{@link ConverterData}.里面可以使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解
  6. * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link ConverterDataListener}
  7. * <p>3. 直接读即可
  8. */
  9. @Test
  10. public void converterRead() {
  11. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  12. // 这里 需要指定读用哪个class去读,然后读取第一个sheet
  13. EasyExcel.read(fileName, ConverterData.class, new ConverterDataListener())
  14. // 这里注意 我们也可以registerConverter来指定自定义转换器, 但是这个转换变成全局了, 所有java为string,excel为string的都会用这个转换器。
  15. // 如果就想单个字段使用请使用@ExcelProperty 指定converter
  16. // .registerConverter(new CustomStringStringConverter())
  17. // 读取sheet
  18. .sheet().doRead();
  19. }

多行头

excel示例

参照:excel示例

对象

参照:对象

监听器

参照:监听器

代码
  1. /**
  2. * 多行头
  3. *
  4. * <p>1. 创建excel对应的实体对象 参照{@link DemoData}
  5. * <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
  6. * <p>3. 设置headRowNumber参数,然后读。 这里要注意headRowNumber如果不指定, 会根据你传入的class的{@link ExcelProperty#value()}里面的表头的数量来决定行数,
  7. * 如果不传入class则默认为1.当然你指定了headRowNumber不管是否传入class都是以你传入的为准。
  8. */
  9. @Test
  10. public void complexHeaderRead() {
  11. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  12. // 这里 需要指定读用哪个class去读,然后读取第一个sheet
  13. EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet()
  14. // 这里可以设置1,因为头就是一行。如果多行头,可以设置其他值。不传入也可以,因为默认会根据DemoData 来解析,他没有指定头,也就是默认1行
  15. .headRowNumber(1).doRead();
  16. }

同步的返回

excel示例

参照:excel示例

对象

参照:对象

代码
  1. /**
  2. * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面
  3. */
  4. @Test
  5. public void synchronousRead() {
  6. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  7. // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
  8. List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
  9. for (DemoData data : list) {
  10. LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
  11. }
  12. // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish
  13. List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
  14. for (Map<Integer, String> data : listMap) {
  15. // 返回每条数据的键值对 表示所在的列 和所在列的值
  16. LOGGER.info("读取到数据:{}", JSON.toJSONString(data));
  17. }
  18. }

读取表头数据

excel示例

参照:excel示例

对象

参照:对象

监听器

参照:监听器
里面多了一个方法,只要重写invokeHeadMap方法即可

  1. /**
  2. * 这里会一行行的返回头
  3. *
  4. * @param headMap
  5. * @param context
  6. */
  7. @Override
  8. public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
  9. LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
  10. }

代码
  1. /**
  2. * 读取表头数据
  3. *
  4. * <p>
  5. * 1. 创建excel对应的实体对象 参照{@link DemoData}
  6. * <p>
  7. * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener}
  8. * <p>
  9. * 3. 直接读即可
  10. */
  11. @Test
  12. public void headerRead() {
  13. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  14. // 这里 需要指定读用哪个class去读,然后读取第一个sheet
  15. EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead();
  16. }

额外信息(批注、超链接、合并单元格信息读取)

since

2.0.0-beta1

excel示例

image.png

对象
  1. @Data
  2. public class DemoExtraData {
  3. private String row1;
  4. private String row2;
  5. }

监听器

参照:监听器
里面多了一个 extra 方法

  1. @Override
  2. public void extra(CellExtra extra, AnalysisContext context) {
  3. LOGGER.info("读取到了一条额外信息:{}", JSON.toJSONString(extra));
  4. switch (extra.getType()) {
  5. case COMMENT:
  6. LOGGER.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(),
  7. extra.getText());
  8. break;
  9. case HYPERLINK:
  10. if ("Sheet1!A1".equals(extra.getText())) {
  11. LOGGER.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(),
  12. extra.getColumnIndex(), extra.getText());
  13. } else if ("Sheet2!A1".equals(extra.getText())) {
  14. LOGGER.info(
  15. "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{},"
  16. + "内容是:{}",
  17. extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
  18. extra.getLastColumnIndex(), extra.getText());
  19. } else {
  20. Assert.fail("Unknown hyperlink!");
  21. }
  22. break;
  23. case MERGE:
  24. LOGGER.info(
  25. "额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}",
  26. extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
  27. extra.getLastColumnIndex());
  28. break;
  29. default:
  30. }
  31. }

代码
  1. /**
  2. * 额外信息(批注、超链接、合并单元格信息读取)
  3. * <p>
  4. * 由于是流式读取,没法在读取到单元格数据的时候直接读取到额外信息,所以只能最后通知哪些单元格有哪些额外信息
  5. *
  6. * <p>
  7. * 1. 创建excel对应的实体对象 参照{@link DemoExtraData}
  8. * <p>
  9. * 2. 由于默认异步读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExtraListener}
  10. * <p>
  11. * 3. 直接读即可
  12. *
  13. * @since 2.2.0-beat1
  14. */
  15. @Test
  16. public void extraRead() {
  17. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "extra.xlsx";
  18. // 这里 需要指定读用哪个class去读,然后读取第一个sheet
  19. EasyExcel.read(fileName, DemoExtraData.class, new DemoExtraListener())
  20. // 需要读取批注 默认不读取
  21. .extraRead(CellExtraTypeEnum.COMMENT)
  22. // 需要读取超链接 默认不读取
  23. .extraRead(CellExtraTypeEnum.HYPERLINK)
  24. // 需要读取合并单元格信息 默认不读取
  25. .extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();
  26. }

读取公式和单元格类型

excel示例

image.png

对象
  1. @Data
  2. public class CellDataReadDemoData {
  3. private CellData<String> string;
  4. // 这里注意 虽然是日期 但是 类型 存储的是number 因为excel 存储的就是number
  5. private CellData<Date> date;
  6. private CellData<Double> doubleData;
  7. // 这里并不一定能完美的获取 有些公式是依赖性的 可能会读不到 这个问题后续会修复
  8. private CellData<String> formulaValue;
  9. }

监听器

参照:监听器
代码

  1. /**
  2. * 读取公式和单元格类型
  3. *
  4. * <p>
  5. * 1. 创建excel对应的实体对象 参照{@link CellDataReadDemoData}
  6. * <p>
  7. * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener}
  8. * <p>
  9. * 3. 直接读即可
  10. *
  11. * @since 2.2.0-beat1
  12. */
  13. @Test
  14. public void cellDataRead() {
  15. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "cellDataDemo.xlsx";
  16. // 这里 需要指定读用哪个class去读,然后读取第一个sheet
  17. EasyExcel.read(fileName, CellDataReadDemoData.class, new CellDataDemoHeadDataListener()).sheet().doRead();
  18. }

读取表头数据

excel示例

参照:excel示例

对象

参照:对象

监听器

参照:监听器
里面多了一个方法,只要重写invokeHeadMap方法即可

  1. /**
  2. * 这里会一行行的返回头
  3. *
  4. * @param headMap
  5. * @param context
  6. */
  7. @Override
  8. public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
  9. LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap));
  10. }

代码
  1. /**
  2. * 读取表头数据
  3. *
  4. * <p>
  5. * 1. 创建excel对应的实体对象 参照{@link DemoData}
  6. * <p>
  7. * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoHeadDataListener}
  8. * <p>
  9. * 3. 直接读即可
  10. */
  11. @Test
  12. public void headerRead() {
  13. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  14. // 这里 需要指定读用哪个class去读,然后读取第一个sheet
  15. EasyExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead();
  16. }

数据转换等异常处理

excel示例

参照:excel示例

对象
  1. @Data
  2. public class ExceptionDemoData {
  3. /**
  4. * 用日期去接字符串 肯定报错
  5. */
  6. private Date date;
  7. }

监听器

参照:监听器
里面多了一个方法,只要重写onException方法即可

  1. /**
  2. * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
  3. *
  4. * @param exception
  5. * @param context
  6. * @throws Exception
  7. */
  8. @Override
  9. public void onException(Exception exception, AnalysisContext context) {
  10. LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
  11. // 如果是某一个单元格的转换异常 能获取到具体行号
  12. // 如果要获取头的信息 配合invokeHeadMap使用
  13. if (exception instanceof ExcelDataConvertException) {
  14. ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
  15. LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
  16. excelDataConvertException.getColumnIndex());
  17. }
  18. }

代码
  1. /**
  2. * 数据转换等异常处理
  3. *
  4. * <p>
  5. * 1. 创建excel对应的实体对象 参照{@link ExceptionDemoData}
  6. * <p>
  7. * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoExceptionListener}
  8. * <p>
  9. * 3. 直接读即可
  10. */
  11. @Test
  12. public void exceptionRead() {
  13. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  14. // 这里 需要指定读用哪个class去读,然后读取第一个sheet
  15. EasyExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead();
  16. }

不创建对象的读

excel示例

参照:excel示例

监听器
  1. /**
  2. * 直接用map接收数据
  3. *
  4. * @author Jiaju Zhuang
  5. */
  6. public class NoModelDataListener extends AnalysisEventListener<Map<Integer, String>> {
  7. private static final Logger LOGGER = LoggerFactory.getLogger(NoModelDataListener.class);
  8. /**
  9. * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
  10. */
  11. private static final int BATCH_COUNT = 5;
  12. List<Map<Integer, String>> list = new ArrayList<Map<Integer, String>>();
  13. @Override
  14. public void invoke(Map<Integer, String> data, AnalysisContext context) {
  15. LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
  16. list.add(data);
  17. if (list.size() >= BATCH_COUNT) {
  18. saveData();
  19. list.clear();
  20. }
  21. }
  22. @Override
  23. public void doAfterAllAnalysed(AnalysisContext context) {
  24. saveData();
  25. LOGGER.info("所有数据解析完成!");
  26. }
  27. /**
  28. * 加上存储数据库
  29. */
  30. private void saveData() {
  31. LOGGER.info("{}条数据,开始存储数据库!", list.size());
  32. LOGGER.info("存储数据库成功!");
  33. }
  34. }

代码
  1. /**
  2. * 不创建对象的读
  3. */
  4. @Test
  5. public void noModelRead() {
  6. String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
  7. // 这里 只要,然后读取第一个sheet 同步读取会自动finish
  8. EasyExcel.read(fileName, new NoModelDataListener()).sheet().doRead();
  9. }

web中的读

示例代码

DEMO代码地址:https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java

excel示例

参照:excel示例

对象

参照:对象 只是名字变了

监听器

参照:监听器 只是泛型变了

代码
  1. /**
  2. * 文件上传
  3. * <p>
  4. * 1. 创建excel对应的实体对象 参照{@link UploadData}
  5. * <p>
  6. * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}
  7. * <p>
  8. * 3. 直接读即可
  9. */
  10. @PostMapping("upload")
  11. @ResponseBody
  12. public String upload(MultipartFile file) throws IOException {
  13. EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
  14. return "success";
  15. }