飞道的博客

Springboot整合EasyExcel,实现Excel文件上传

353人阅读  评论(0)

一、概念

EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。
它能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。EasyExcel是在尽可能节约内存的情况下支持读写百M的Excel。

二、Excel的上传(读Excel)

1.Excel读取的实现方案

实现Springboot结合EasyExcel实现对Excel中数据的读取,并且将读取的数据通过Mybatis-plus保存到Mysql数据库。

2. maven依赖,pom文件

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.4.2</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>3.1.1</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.8</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.76</version>
    </dependency>
</dependencies>
  

3. pom文件,加载xml文件

由于用到MyBatisplus,所以一定不要忘记加下面的这段代码,否则你的mapper是编译不到你的classpath中的。

<build>
    <!-- 由于用到MyBatis,所以一定不要忘记加下面的这段代码,否则你的mapper是编译不到你的classpath中的。-->
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
    </resources>
</build>

4. application.yml文件配置mybatis-plus

server:
  port: 80

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/redhorse?serverTimezone=UTC
      username: root
      password: root

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

5. 表格和表对应的实体类

既然要读取Excel,同时存入数据库,那么就必然需要对应的表,以及表对应的实体类,而Excel也需要对应的实体类。因为Excel表格会增加一些不必要的字段,而这些字段并不需要存入数据库中,同理数据库实体类同样存在一些字段不是从表格中获取。

5.1 表格对应的实体类


  
  1. package com.atorientsec.entities;
  2. import com.alibaba.excel.annotation.ExcelProperty;
  3. import com.alibaba.excel.annotation.format.DateTimeFormat;
  4. import com.alibaba.excel.annotation.format.NumberFormat;
  5. import lombok.Data;
  6. @Data
  7. public class ExcelAttdnOver {
  8. @ExcelProperty(index =2)
  9. private String department;
  10. @ExcelProperty(index = 0)
  11. private String name;
  12. /**
  13. * 这里用String去接日期,才能格式化,接收年月日的格式
  14. */
  15. @ExcelProperty(index = 3)
  16. @DateTimeFormat(value = "yyyy-MM-dd")
  17. private String overDate;
  18. @ExcelProperty(index = 4)
  19. private Double overHours;
  20. /**
  21. *接收百分比的数字
  22. */
  23. @ExcelProperty(index = 7)
  24. @NumberFormat("#.##%")
  25. private String rate;
  26. }

5.2 数据库对应的实体类 


  
  1. package com.atorientsec.entities;
  2. import com.baomidou.mybatisplus.annotation.IdType;
  3. import com.baomidou.mybatisplus.annotation.TableId;
  4. import lombok.Data;
  5. import org.springframework.format.annotation.DateTimeFormat;
  6. import java.util.Date;
  7. @Data
  8. public class AttdnOver {
  9. @TableId(type = IdType.AUTO)
  10. private Integer id;
  11. private String department;
  12. private String name;
  13. private String attdnMonth;
  14. @DateTimeFormat(pattern = "yyyy-MM-dd")
  15. private Date overDate;
  16. private Double overHours;
  17. private Double rate;
  18. }

注释:数据库实体类的overDate是Date类型,而Excel对应的类中overDate是String类型,只有String去接日期才能格式化。

6. 默认一行行的读取excel,所以需要创建excel一行一行的回调监听器


  
  1. package com.atorientsec.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.atorientsec.entities.AttdnOver;
  7. import com.atorientsec.entities.ExcelAttdnOver;
  8. import com.atorientsec.service.AttdnOverService;
  9. import lombok.extern.slf4j.Slf4j;
  10. import java.text.SimpleDateFormat;
  11. import java.util.*;
  12. @Slf4j
  13. public class AttdnDataListener implements ReadListener<ExcelAttdnOver> {
  14. /**
  15. * Excel模板的读取类
  16. * 有个很重要的点,AttdnDataListener不能被Spring管理
  17. * 要每次读取excel都要new,然后里面用到spring可以构造方法传进去
  18. * @param excelAttdnOver
  19. * @param analysisContext
  20. */
  21. /**
  22. * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
  23. */
  24. private static final int BATCH_COUNT = 100;
  25. private int count = 0;
  26. /**
  27. * 缓存的数据,List<AttdnOver>
  28. */
  29. private List<AttdnOver> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
  30. private AttdnOverService attdnOverService;
  31. private String month;
  32. public AttdnDataListener (){
  33. }
  34. /**
  35. * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
  36. *
  37. */
  38. public AttdnDataListener (AttdnOverService attdnOverService,String month){
  39. this.attdnOverService = attdnOverService;
  40. this.month = month;
  41. }
  42. /**
  43. * 这个每一条数据解析都会来调用
  44. *
  45. */
  46. @Override
  47. public void invoke (ExcelAttdnOver excelAttdnOver, AnalysisContext analysisContext) {
  48. log.info( "解析到第 {} 条数据:{}", (++count), JSON.toJSONString(excelAttdnOver));
  49. try {
  50. //把表格对应的实体类对象转化成数据库表对应的对象
  51. AttdnOver attdnOver = new AttdnOver();
  52. attdnOver.setDepartment(excelAttdnOver.getDepartment());
  53. attdnOver.setName(excelAttdnOver.getName());
  54. attdnOver.setAttdnMonth( this.month);
  55. SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd");
  56. Date date = sdf.parse(excelAttdnOver.getOverDate());
  57. attdnOver.setOverDate(date);
  58. attdnOver.setOverHours(excelAttdnOver.getOverHours());
  59. attdnOver.setRate(Double.parseDouble(excelAttdnOver.getRate().replace( "%", "")));
  60. cachedDataList.add(attdnOver);
  61. if(cachedDataList.size()>=BATCH_COUNT){
  62. saveData();
  63. // 存储完成清理 list
  64. cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
  65. }
  66. } catch (Exception e){
  67. log.error(e.getMessage());
  68. }
  69. }
  70. /**
  71. * 接收表头信息
  72. @Override
  73. public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
  74. Map<Integer,String> StringMap = ConverterUtils.convertToStringMap(headMap,context);
  75. Set<Integer> keySet = StringMap.keySet();
  76. System.out.println("该Excel表头信息是:");
  77. for(int i=0;i<keySet.size();i++){
  78. System.out.println("第 "+(i+1)+" 列 = "+StringMap.get(i));
  79. }
  80. ReadListener.super.invokeHead(headMap, context);
  81. }
  82. */
  83. /**
  84. * 所有数据解析完成了 都会来调用
  85. * @param analysisContext
  86. */
  87. @Override
  88. public void doAfterAllAnalysed (AnalysisContext analysisContext) {
  89. log.info( "表格中的所有数据解析完成!!!");
  90. // 这里也要保存数据,确保最后遗留的数据也存储到数据库
  91. saveData();
  92. }
  93. /**
  94. * 存储数据库
  95. */
  96. private void saveData () {
  97. log.info( "{}条数据,开始存储数据库!", cachedDataList.size());
  98. attdnOverService.batchSave(cachedDataList);
  99. log.info( "存储数据库成功!");
  100. }
  101. }

7. mapper-继承Mybatis-plus的baseMapper


  
  1. package com.atorientsec.mapper;
  2. import com.atorientsec.entities.AttdnOver;
  3. import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  4. import org.apache.ibatis.annotations.Mapper;
  5. @Mapper
  6. public interface AttdnOverMapper extends BaseMapper<AttdnOver> {
  7. }

8. service:Mybatis-plus实现批量插入,并开启事务


  
  1. package com.atorientsec.service;
  2. import com.atorientsec.entities.AttdnOver;
  3. import com.baomidou.mybatisplus.extension.service.IService;
  4. import org.springframework.transaction.annotation.Transactional;
  5. import java.util.ArrayList;
  6. public interface AttdnOverService extends IService<AttdnOver> {
  7. @Transactional
  8. boolean batchSave (ArrayList<AttdnOver> attdnOverArrayList);
  9. }

  
  1. package com.atorientsec.service.impl;
  2. import com.atorientsec.entities.AttdnOver;
  3. import com.atorientsec.mapper.AttdnOverMapper;
  4. import com.atorientsec.service.AttdnOverService;
  5. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  6. import org.springframework.stereotype.Service;
  7. import java.util.ArrayList;
  8. @Service
  9. public class AttdnOverServiceImpl extends ServiceImpl<AttdnOverMapper, AttdnOver> implements AttdnOverService {
  10. public boolean batchSave (ArrayList<AttdnOver> attdnOverArrayList){
  11. //saveBatch是mybatisplus的批量插入方法
  12. boolean status = saveBatch(attdnOverArrayList);
  13. return status;
  14. }
  15. }

9. Controller:MultipartFile上传文件

EasyExcel读文件


  
  1. package com.atorientsec.controller;
  2. import com.alibaba.excel.EasyExcel;
  3. import com.atorientsec.entities.ExcelAttdnOver;
  4. import com.atorientsec.listener.AttdnDataListener;
  5. import com.atorientsec.service.AttdnOverService;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. import org.springframework.web.bind.annotation.PostMapping;
  9. import org.springframework.web.bind.annotation.RequestMapping;
  10. import org.springframework.web.bind.annotation.RestController;
  11. import org.springframework.web.multipart.MultipartFile;
  12. import java.io.IOException;
  13. @RestController
  14. @RequestMapping("/excel")
  15. public class AttdnOverController {
  16. @Autowired
  17. private AttdnOverService attdnOverService;
  18. @PostMapping("/upload/{month}")
  19. public String upload (MultipartFile file, @PathVariable String month) throws IOException {
  20. // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
  21. EasyExcel.read(file.getInputStream(), ExcelAttdnOver.class, new AttdnDataListener(attdnOverService,month))
  22. .sheet().headRowNumber( 1).doRead();
  23. return "success";
  24. }
  25. }

三、postman测试文件上传

1. postman设置Header的key=Content-Type,Value=multipart/form-data

2. postman设置Body的key=file,并选择为File,value选择目录

 注释:key=file,此处的file变量与Java代码的Controller中的MultipartFile file变量名保持一样,否则不起作用,读不到文件。

@PostMapping("/upload/{month}") public String upload(MultipartFile file, @PathVariable String month) {}


转载:https://blog.csdn.net/weixin_51725434/article/details/128306711
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场