• 使用EasyExcel实现Excel的导入导出



    前言

    在真实的开发者场景中,经常会使用excel作为数据的载体,进行数据导入和导出的操作,使用excel的导入和导出有很多种解决方案,本篇记录一下EasyExcel的使用。


    一、EasyExcel是什么?

    EasyExcel是一个开源的项目,是阿里开发的。EasyExcel可以简化Excel表格的导入和导出操作,使用起来简单快捷,易上手。

    二、使用步骤

    1.导入依赖

    在pom.xml中导入我们需要使用的依赖

     
        <dependency>
          <groupId>commons-fileuploadgroupId>
          <artifactId>commons-fileuploadartifactId>
          <version>1.4version>
        dependency>
         
        <dependency>
          <groupId>com.alibabagroupId>
          <artifactId>easyexcelartifactId>
          <version>2.1.6version>
        dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.编写文件上传配置

    在springMVC配置文件中添加如下配置

    
        <bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
            
            <property name="defaultEncoding" value="utf-8"/>
            
            <property name="maxUploadSize" value="10485760"/>
        bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.配置表头对应实体类

    package com.lzl.entityBo;
    
    import com.alibaba.excel.annotation.ExcelIgnore;
    import com.alibaba.excel.annotation.ExcelProperty;
    import com.alibaba.excel.annotation.format.DateTimeFormat;
    
    
    /**
     * --效率,是成功的核心关键--
     *
     * @Author lzl
     * @Date 2022/10/29 13:59
     */
    
    public class EmployeeBo {
        @ExcelProperty(value = "工号")//表头注解
        private Integer emp_no;
        @ExcelProperty(value = "姓名")
        private String emp_name;
        @ExcelProperty(value = "性别")
        private String  emp_gender;
        @ExcelProperty(value = "年龄")
        private Integer emp_age;
        @ExcelIgnore//不输出该列
        private Integer emp_dept_no;
        @ExcelProperty(value = "薪资")
        private Double emp_salary;
        @ExcelProperty(value = "入职时间")
        @DateTimeFormat("yyyy-MM-dd")//格式化日期时间
        private String emp_hire_time;
        @ExcelProperty(value = "职位")
        private String emp_position;
        @ExcelProperty(value = "状态")
        private Integer emp_status;
    
        public EmployeeBo() {
        }
    
        public EmployeeBo(Integer emp_no, String emp_name, String emp_gender, Integer emp_age, Integer emp_dept_no, Double emp_salary, String emp_hire_time, String emp_position, Integer emp_status) {
            this.emp_no = emp_no;
            this.emp_name = emp_name;
            this.emp_gender = emp_gender;
            this.emp_age = emp_age;
            this.emp_dept_no = emp_dept_no;
            this.emp_salary = emp_salary;
            this.emp_hire_time = emp_hire_time;
            this.emp_position = emp_position;
            this.emp_status = emp_status;
        }
    
        public Integer getEmp_no() {
            return emp_no;
        }
    
        public void setEmp_no(Integer emp_no) {
            this.emp_no = emp_no;
        }
    
        public String getEmp_name() {
            return emp_name;
        }
    
        public void setEmp_name(String emp_name) {
            this.emp_name = emp_name;
        }
    
        public String getEmp_gender() {
            return emp_gender;
        }
    
        public void setEmp_gender(String emp_gender) {
            this.emp_gender = emp_gender;
        }
    
        public Integer getEmp_age() {
            return emp_age;
        }
    
        public void setEmp_age(Integer emp_age) {
            this.emp_age = emp_age;
        }
    
        public Integer getEmp_dept_no() {
            return emp_dept_no;
        }
    
        public void setEmp_dept_no(Integer emp_dept_no) {
            this.emp_dept_no = emp_dept_no;
        }
    
        public Double getEmp_salary() {
            return emp_salary;
        }
    
        public void setEmp_salary(Double emp_salary) {
            this.emp_salary = emp_salary;
        }
    
        public String getEmp_hire_time() {
            return emp_hire_time;
        }
    
        public void setEmp_hire_time(String emp_hire_time) {
            this.emp_hire_time = emp_hire_time;
        }
    
        public String getEmp_position() {
            return emp_position;
        }
    
        public void setEmp_position(String emp_position) {
            this.emp_position = emp_position;
        }
    
        public Integer getEmp_status() {
            return emp_status;
        }
    
        public void setEmp_status(Integer emp_status) {
            this.emp_status = emp_status;
        }
    
    
    
        @Override
        public String toString() {
            return "EmployeeBo{" +
                    "emp_no=" + emp_no +
                    ", emp_name='" + emp_name + '\'' +
                    ", emp_gender='" + emp_gender + '\'' +
                    ", emp_age=" + emp_age +
                    ", emp_dept_no=" + emp_dept_no +
                    ", emp_salary=" + emp_salary +
                    ", emp_hire_time='" + emp_hire_time + '\'' +
                    ", emp_position='" + emp_position + '\'' +
                    ", emp_status=" + emp_status +
                    '}';
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140

    4.监听器编写

    使用EasyExcel需要进行全局监听配置
    新建一个listener包,并在该包下新建一个MyListener类,继承AnalysisEventListener类,泛型设置为当前需要导出的类,我这里需要导出的是员工数据,因此我将泛型设置为员工类,整体代码如下:

    package com.lzl.listener;
    
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.event.AnalysisEventListener;
    import com.lzl.entity.Employee;
    import com.lzl.entityBo.EmployeeBo;
    import com.lzl.service.EmployeeService;
    import org.springframework.beans.BeanUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Component;
    
    //easy excel 监听器
    @Component
    @Scope("prototype")//标记此处的监听器为多例的,防止并发读操作时出现错误
    public class EmployeeReadListener extends AnalysisEventListener<EmployeeBo> {
    
        //此处自动注入业务层的新增功能
        @Autowired
        private EmployeeService service;
    
        @Override
        public void invoke(EmployeeBo data, AnalysisContext analysisContext) {
            //每读取一行数据都会调用一次,会把每一行数据封装到employee
            //属性不一致时,使用属性拷贝
            Employee employee = new Employee();
            BeanUtils.copyProperties(data,employee);
            service.addNewEmployee(employee);//调用新增方法
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            //所有数据解析完毕执行该方法
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    此处监听器的作用是,当我们每次从excel表格中读取一行数据时,我们都需要进行新增操作,而新增操作交给我们的监听器去完成。

    5.控制层

    控制层负责接收前端传过来的文件流,然后使用EasyExcel来对导入或者导出的数据进行进一步的处理,并把处理结果返回,需要注意的是,对于导出操作,该方法并没有返回值,因此我们不需要返回值,而对于导入方法,此处我使用的是layui的前端,所以返回的数据格式是layui可以接收的工具类。

    package com.lzl.controller;
    
    import com.alibaba.excel.EasyExcel;
    import com.alibaba.excel.read.builder.ExcelReaderBuilder;
    import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
    import com.alibaba.excel.write.builder.ExcelWriterBuilder;
    import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
    import com.lzl.entityBo.EmployeeBo;
    import com.lzl.listener.EmployeeReadListener;
    import com.lzl.service.EmployeeService;
    import com.lzl.util.ResultLayUi;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.multipart.MultipartFile;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    import java.net.URLEncoder;
    import java.util.List;
    
    
    @Controller
    @RequestMapping("/excel")
    public class UploadExcelController {
    
        //注入监听器
        @Autowired
        private EmployeeReadListener listener;
    
        //注入业务层
        @Autowired
        private EmployeeService service;
        /**
         *
         * @param file 获得前端上传的文件  EasyExcel.read 需要传入三个参数 文件流 操作实体类的字节码 监听器
         * @return 0 成功上传
         * @throws IOException
         */
        @RequestMapping("read")
        @ResponseBody
        public ResultLayUi<String> readExcel(MultipartFile file) throws IOException {
            // 得到excel读取对象  //通过文件获得流, 获得读取文件的class    填入监听器 监听器每读取一行就执行一次新增
            ExcelReaderBuilder read = EasyExcel.read(file.getInputStream(), EmployeeBo.class, listener);
            //获取表格
            ExcelReaderSheetBuilder sheet = read.sheet();
            //读取表格
            sheet.doRead();
            ResultLayUi<String> resultLayUi = new ResultLayUi<String>();
            resultLayUi.setCode(0);
            resultLayUi.setMsg("已成功导入");
            return resultLayUi;
        }
        @RequestMapping("write")
        public void writeExcel(String nos,HttpServletResponse response) throws IOException {
            //设置响应头
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            //导出的文件名
            String filename = URLEncoder.encode("员工信息","utf-8");
            //设置响应头
            response.setHeader("Content-Disposition","attachment;filename="+filename+".xlsx");
            //获得流对象
            ServletOutputStream outputStream = response.getOutputStream();
            //获得write对象
            ExcelWriterBuilder write = EasyExcel.write(outputStream, EmployeeBo.class);
            //获得数据表对象
            ExcelWriterSheetBuilder sheet = write.sheet();
            //准备需要输出的数据   调用业务层,获得所有需要导出的数据
            List<EmployeeBo> list = service.getExcelDataByNos(nos);
            //生成表格文件
            sheet.doWrite(list);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    6.前端代码

    前端使用的是layui的文件上传组件。所有相关代码如下:

    下载:这里我使用了switch-case分支来对多个按钮的事件进行区分和绑定,
    layui的组件加载:

      layui.use(['table', 'layer', 'form', 'laydate', 'transfer','upload','element'], function () {
          var table = layui.table;
          var layer = layui.layer;
          var form = layui.form;
          var laydate = layui.laydate;
          var transfer = layui.transfer;
          var upload = layui.upload;
          var element = layui.element;
          var $ = layui.$;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    按钮:

      	  <button class="layui-btn layui-btn-xs layui-btn-normal"  lay-event="write">
            <i class="layui-icon">i>导出数据
          button>
          <button class="layui-btn layui-btn-xs layui-btn-normal"  lay-event="read">
            <i class="layui-icon">i>导入数据
          button>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上传弹出层:

     
      <div class="layui-form-item" id="import" style="display:none;">
        <form class="layui-form" onsubmit="return false;" id="readExcel">
          <div class="layui-form-item">
            <label class="layui-form-label">label>
            <div class="layui-input-block">
              <div class="layui-upload">
                <button type="button" class="layui-btn" id="test1">上传文件button>
                <div style="width: 95px;">
                  <div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="demo">
                    <div class="layui-progress-bar" lay-percent="">div>
                  div>
                div>
              div>
            div>
          div>
        form>
      div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
     case 'write':
                if (data.length === 0) {
                  layer.msg('请至少选择一行');
                } else if (data.length > 1) {
                  $list = checkStatus.data;
                  $ids = [];
                  for (var i = 0; i < $list.length; i++) {
                    $ids[i] = $list[i].emp_no;
                  }
                  $strIds = ""
                  for (var i = 0; i < $ids.length; i++) {
                    $strIds += $ids[i] + ",";
                  }
                  layer.confirm("确认导出所有选中行吗?", function (index) {
                    window.location.href = "http://localhost:8080/excel/write?nos=" + $strIds;
                    layer.close(index);
                  });
                }
                break;
    case 'read':
              layer.open({
                type: 1,
                title: '上传excel文件',
                area: ['300px', '300px'],
                content: $('#import')
                //这里content是一个DOM,注意:最好该元素要存放在body最外层,否则可能被其它的相对元素所影响
              });
                break;
    
    		//上传文件的事件:
    
          //常规使用 - excel表格上传
         upload.render({
            elem: '#test1'
            ,accept: 'file' //普通文件
            ,exts: 'xlsx' //上传文件格式
            , url: 'http://localhost:8080/excel/read' 
            , done: function (res) {
              console.log(res);
              //如果上传失败
              if (res.code > 0) {
                layer.msg('上传失败');
              } else {
                active.reload();
                layer.msg(res.msg, {
                  icon: 6,
                  time: 1500 //2秒关闭(如果不配置,默认是3秒)
                }, function () {
                  //关闭弹出层
                  layer.closeAll();
                  element.progress('demo', '0%'); //进度条复位
                });
              }
            }
            //进度条
            , progress: function (n, elem, e) {
              element.progress('demo', n + '%'); //可配合 layui 进度条元素使用
              if (n == 100) {
                //layer.msg('上传完毕', { icon: 1 });
              }
            }
          });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    总结

    EasyExcel是一个excel导入导出的解决方案,非常好用。

  • 相关阅读:
    主线程和子线程的关系(讨论主线程结束,子线程是否要回收)
    Jeewx-api 1.4.9版本发布—第三方APP开发SDK,支持微信、钉钉、企业微信、小程序等
    7、JProfiler工具分析OOM原因
    【Python】循环语句 ① ( while 循环语法 | 代码示例 1 - while 循环操作 | 代码示例 2 - while 循环求和 | 代码示例 3 - 猜数字 )
    十种常见排序算法详解-java
    基于安卓android微信小程序的旅游系统
    ContextMenuStrip内容菜单源对象赋值学习笔记(含源码)
    vue3 +element-plus中避免一打开表单的下拉选择的change事件自动校验问题
    程序员脱单
    【经典算法学习-排序篇】直接选择排序
  • 原文地址:https://blog.csdn.net/l_zl2021/article/details/127589481