参考:2小时快速掌握EasyExcel报表导入导出-黑马程序员杭州校区出品
https://www.cnblogs.com/yinrz/p/15272431.html
阿里官方使用说明

引入依赖

  1. <!-- excel导入和导出插件-->
  2. <dependency>
  3. <groupId>com.alibaba</groupId>
  4. <artifactId>easyexcel</artifactId>
  5. <version>3.1.0</version>
  6. </dependency>

读取excel表格文件方法

第一步、定义一个java实体类用来接收数据

第二步、设置监听器

  1. package com.tj.demo.system.listener;
  2. import com.alibaba.excel.context.AnalysisContext;
  3. import com.alibaba.excel.read.listener.ReadListener;
  4. import com.alibaba.excel.util.ListUtils;
  5. import com.alibaba.fastjson.JSON;
  6. import com.tj.demo.system.domain.Users;
  7. import lombok.extern.slf4j.Slf4j;
  8. import java.util.List;
  9. // 有个很重要的点 UsersListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
  10. @Slf4j
  11. public class UsersListener implements ReadListener<Users> {
  12. /**
  13. * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
  14. */
  15. private static final int BATCH_COUNT = 100;
  16. /**
  17. * 缓存的数据
  18. */
  19. private List<Users> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
  20. /**
  21. * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
  22. */
  23. private Users users;
  24. public UsersListener() {
  25. // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
  26. users = new Users();
  27. }
  28. /**
  29. * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
  30. *
  31. * @param users
  32. */
  33. public UsersListener(Users users) {
  34. this.users = users;
  35. }
  36. /**
  37. * 这个每一条数据解析都会来调用
  38. *
  39. * @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
  40. * @param context
  41. */
  42. @Override
  43. public void invoke(Users data, AnalysisContext context) {
  44. log.info("解析到一条数据:{}", JSON.toJSONString(data));
  45. cachedDataList.add(data);
  46. // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
  47. if (cachedDataList.size() >= BATCH_COUNT) {
  48. saveData();
  49. // 存储完成清理 list
  50. cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
  51. }
  52. }
  53. /**
  54. * 所有数据解析完成了 都会来调用
  55. *
  56. * @param context
  57. */
  58. @Override
  59. public void doAfterAllAnalysed(AnalysisContext context) {
  60. // 这里也要保存数据,确保最后遗留的数据也存储到数据库
  61. saveData();
  62. log.info("所有数据解析完成!");
  63. }
  64. /**
  65. * 自定义处理数据的方法加上存储数据库
  66. */
  67. private void saveData() {
  68. log.info("{}条数据,开始存储数据库!", cachedDataList.size());
  69. //这个去处理数据保存,还是干嘛
  70. //.......................
  71. log.info(cachedDataList.toString());
  72. log.info("存储数据库成功!");
  73. }
  74. }

第三步、开始读取

  1. @Test
  2. public void testReadExcel() {
  3. /*
  4. 获得一个工作簿读对象
  5. 参数一:Excel文件路径
  6. 参数二:每行数据对应的实体类型
  7. 参数三:读监听器,每读一行就会调用一次该监听器的invoke方法
  8. */
  9. ExcelReaderBuilder readWorkBook = EasyExcel.read("C:/Users/心有灵茜/Desktop/新建文件夹/users.xls", Users.class, new UsersListener());
  10. // 获得一个工作表对象
  11. ExcelReaderSheetBuilder sheet = readWorkBook.sheet();
  12. // 读取工作表中的内容
  13. sheet.doRead();
  14. }
  1. /**
  2. * 最简单的读(官方写法)
  3. * 1. 创建excel对应的实体对象 参照{@link Users}
  4. * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UsersListener}
  5. * 3. 直接读即可
  6. */
  7. @Test
  8. public void simpleRead() {
  9. //文件名含路径
  10. String fileName = "C:/Users/心有灵茜/Desktop/新建文件夹/users.xls";
  11. // 有个很重要的点 UsersListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
  12. // 写法3(选用官方的第三种简单写法):
  13. // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
  14. EasyExcel.read(fileName, Users.class, new UsersListener()).sheet().doRead();
  15. }

导出excel表格

第一步、在java实体类指定导出名称

第二步、获取数据直接导出

  1. @Autowired
  2. private UsersService usersService;
  3. @Test
  4. public void testWriteExcel() {
  5. /*
  6. 获得一个工作簿写对象
  7. 参数一:导出的Excel文件路径
  8. 参数二:每行数据对应的实体类型
  9. */
  10. String pathName="C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";
  11. ExcelWriterBuilder writeWorkBook = EasyExcel.write(pathName, Users.class);
  12. // 获得一个工作表对象
  13. ExcelWriterSheetBuilder sheet = writeWorkBook.sheet();
  14. // 准备要导出的数据
  15. List<Users> list = usersService.list();
  16. // 写入工作表
  17. sheet.doWrite(list);
  18. }
  1. @Test
  2. public void simpleWriteExcel() {
  3. /**
  4. * 最简单的写
  5. * 1. 创建excel对应的实体对象
  6. * 2. 直接写即可
  7. */
  8. String pathName = "C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";
  9. // 准备要导出的数据
  10. List<Users> list = usersService.list();
  11. // 写法2
  12. // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
  13. // 如果这里想使用03 则 传入excelType参数即可
  14. EasyExcel.write(pathName, Users.class).sheet("sheet1").doWrite(list);
  15. }

注解参数说明

  1. //这个注解可以指定导出的表头名称和导入的表头名称
  2. //value是表头名称,index是列的位置排序,index不设置,是默认的排序
  3. @ExcelProperty(value="帐号",index=2)
  4. //指定不导出的列
  5. @ExcelIgnore()
  6. //日期格式化
  7. @DateTimeFormat("yyyy年MM月dd日")
  8. //数字格式化
  9. @NumberFormat
  10. // 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与
  11. @ExcelIgnoreUnannotated
  12. /**
  13. *样式设置
  14. */
  15. //设置导出的列宽@ColumnWidth(20)
  16. @ColumnWidth(20)
  17. //表头行高设定,要写在class类上,不是字段上
  18. @HeadRowHeight(10)
  19. //字段内容行高设定,要写在class类上,不是字段上
  20. @ContentRowHeight(20)

自定义转换器

参考:EasyExcel导出-自定义转换器,将某个字段值转换成字符串

复杂表头设置

效果图
image.png

  1. // @ExcelProperty的value是一个字符串数组,默认相同的会合并到一起
  2. @ExcelProperty({"个人信息","帐号"})
  3. private String user_accout;
  4. @ExcelProperty({"个人信息","姓名"})
  5. private String user_name;

Web上传和下载方法

下载二次封装

  1. /**
  2. * 通过EasyExcel导出表格文件(下载失败了会返回一个有部分数据的Excel)
  3. * 1. 创建excel对应的实体对象
  4. * 2. 设置返回的 参数
  5. * 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
  6. *
  7. * @param response 响应输出
  8. * @param head Java实体类
  9. * @param data 导出data数据
  10. * @throws IOException
  11. */
  12. public static void downxlsxByEasyexcel(HttpServletResponse response, Class head, Collection<?> data) throws IOException {
  13. //设置返回文件类型
  14. response.setContentType("application/vnd.ms-excel");
  15. //设置编码格式utf-8
  16. response.setCharacterEncoding("utf-8");
  17. //这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
  18. String fileName = URLEncoder.encode("导出xlsx-" + TjDateUtils.getTodayString(), "UTF-8");
  19. response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
  20. EasyExcel.write(response.getOutputStream(), head).sheet("sheet1").doWrite(data);
  21. }

用模版下载二次封装

  1. /**
  2. * 根据自己设置的模板文件,通过EasyExcel导出表格文件
  3. *
  4. * @param response 输出流
  5. * @param templateFileName 模板文件名称
  6. * @param data 数据
  7. * @throws UnsupportedEncodingException
  8. */
  9. public static void downxlsxByEasyexcelWithTemplate(HttpServletResponse response, String templateFileName, Collection<?> data) throws UnsupportedEncodingException {
  10. // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
  11. // {} 代表普通变量 {.} 代表是list的变量
  12. //设置返回文件类型
  13. response.setContentType("application/vnd.ms-excel");
  14. //设置编码格式utf-8
  15. response.setCharacterEncoding("utf-8");
  16. // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
  17. String fileName = URLEncoder.encode("导出xlsx-" + TjDateUtils.getTodayString(), "UTF-8");
  18. response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
  19. // 方案1 : 使用 try-with-resources @since 3.1.0
  20. try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(templateFileName).build()) {
  21. WriteSheet writeSheet = EasyExcel.writerSheet().build();
  22. // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
  23. // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
  24. // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
  25. FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
  26. //填充数据
  27. excelWriter.fill(data, fillConfig, writeSheet);
  28. } catch (IOException e) {
  29. e.printStackTrace();
  30. }
  31. }

根据模版填充表格

单组数据填充

模板

利用EasyExcel读写表格文件 - 图2

最终效果

利用EasyExcel读写表格文件 - 图3

  1. @Test
  2. public void testWriteWithTempalte() {
  3. //文件保存路径
  4. String pathName = "C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";
  5. //获取模板的文件路径
  6. String templatePath = "template/users.xlsx";
  7. //创建一个工作簿对象
  8. ExcelWriterBuilder writerBuilder = EasyExcel.write(pathName, Users.class).withTemplate(templatePath);
  9. //创建工作表对象
  10. ExcelWriterSheetBuilder sheet = writerBuilder.sheet();
  11. //方法1,准备对象数据,使用对象
  12. Users users = new Users();
  13. users.setUser_accout("ce");
  14. users.setUser_name("1111");
  15. //方法2,准备Map数据
  16. Map<String, Object> map = new HashMap<String, Object>();
  17. map.put("user_accout", "张三");
  18. map.put("user_name", 5.2);
  19. //填充数据
  20. sheet.doFill(users);
  21. }

多组数据填充列表

多行模板

利用EasyExcel读写表格文件 - 图4

复杂填充模板

利用EasyExcel读写表格文件 - 图5

  1. /**
  2. * 复杂的填充
  3. *
  4. * @since 2.1.1
  5. */
  6. @Test
  7. public void complexFill() {
  8. // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
  9. // {} 代表普通变量 {.} 代表是list的变量
  10. //文件保存路径
  11. String fileName = "C:/Users/心有灵茜/Desktop/新建文件夹/用户信息导出.xlsx";
  12. //获取模板的文件路径
  13. String templateFileName = "template/users.xlsx";
  14. // 方案1 : 使用 try-with-resources @since 3.1.0
  15. try (ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(templateFileName).build()) {
  16. WriteSheet writeSheet = EasyExcel.writerSheet().build();
  17. // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
  18. // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
  19. // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
  20. // 如果数据量大 list不是最后一行 参照下一个
  21. FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
  22. //准备数据
  23. List<Users> list = usersService.list();
  24. excelWriter.fill(list, fillConfig, writeSheet);
  25. Map<String, Object> map = MapUtils.newHashMap();
  26. map.put("date", "2019年10月9日13:28:28");
  27. map.put("total", 1000);
  28. excelWriter.fill(map, writeSheet);
  29. }
  30. }

模板

image.png

最终效果

image.png