Skip to content

EasyExcel

1.easyexcel介绍

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。 github地址:https://github.com/alibaba/easyexcel

特点

  • Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
  • EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
  • EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。

2.快速入门

2.1 创建maven 工程

2.2 添加依赖

shell
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.glls</groupId>
    <artifactId>easyexcel</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>
</project>

2.3 单实体导入

2.3.1创建实体类

java
package com.glls.easyexcel.pojo;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @ClassName : Student

 * @Description :   实体类中定义属性的顺序  和  excel 中的 列顺序 有对应关系
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
   
    private String id;

    private String name;

    
    private String sex;

  
    private Date birthday;
}

2.3.2 准备一个 excel 文件

image-20210821172517065

2.3.3 测试读数据

java
 //简单读    excel 数据
    @Test
    public void test01(){
        //获得一个工作簿对象    .xls
        //param1 文件路径
        //param2 文件中每行数据y要存储的实体类类型
        //param3 监听器   每读取一行数据 都会调用监听器的 invoke 方法 ,在invoke 方法中可以获取读取到的数据
        ExcelReaderBuilder excelReaderBuilder = EasyExcel.read("f://测试easyexcel.xlsx", Student.class, new StudentReadListener());
        //获得一个工作表对象    sheet
        ExcelReaderSheetBuilder sheet = excelReaderBuilder.sheet();
        // 读取工作表中的内容
        sheet.doRead();
    }
java
package com.glls.easyexcel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.glls.easyexcel.pojo.Student;

/**
 * @ClassName : StudentListener

 * @Description :
 */
public class StudentReadListener extends AnalysisEventListener<Student> {
    //每读取一行数据 会调用一次这个方法
    @Override
    public void invoke(Student student, AnalysisContext analysisContext) {
        System.out.println(student);

    }

    //全部读取完之后 会调用该方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        System.out.println("读取完毕");
    }
}

2.4 添加日志的配置文件 log4j.properties

properties
log4j.rootLogger=debug, Console 
#file  Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
#log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
#log4j.appender.file.File=e:/my.log
#log4j.appender.file.Append=true
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n
log4j.logger.org.apache.ibatis=info

测试结果

image-20210821172854505

2.5 写数据到excel 中

java
 //简单写   写数据到  excel 中
    @Test
    public void test02(){
        //工作簿对象
        ExcelWriterBuilder writerBuilder = EasyExcel.write("f://测试easyexcel-write.xlsx", Student.class);

        //工作表对象
        ExcelWriterSheetBuilder sheet = writerBuilder.sheet();

        //写
        List<Student> students = Arrays.asList(new Student("1","zs","男",new Date()), new Student("2","ls","女",new Date()));
        sheet.doWrite(students);

    }
  • xls 版本的Excel最多一次可写0 ...65535行

  • xlsx 版本的Excel最多一次可写0...1048575行

2.6 常用注解

@ExcelProperty   作用在成员变量
属性  value     指定列头名   默认列头是属性名
	 index      指定属性在 excel 中的第几列   默认 按照属性顺序
	 converter  成员变量转换器 自定义转换器需要实现Converter 接口
	建议  index 和  value 要对应着使用
@ColumnWidth
	 value    指定 列宽


@ExcelIgnore   忽略某个属性   不把某一个属性写入excel

@DateTimeFormat("yyyy-MM-dd")    日期格式化


@ContentRowHeight     内容行高   只能作用于类
@HeadRowHeight   列头行高   只能作用于类

2.7 常用API

java
EasyExcel  入口类 用于构建各种对象 开始各种操作 
ExcelReaderBuilder     构建出一个   ReadWorkbook 对象  是其属性 即一个工作簿对象 对应一个 excel 文件
ExcelWriterBuilder     构建出一个 WriteWorkbook 对象  是其属性  即一个工作簿对象 对应一个 excel 文件
ExcelReaderSheetBuilder   构建一个 ReadSheet 对象  即一个工作表对象 对应excel 中的 sheet 页 ,一个工作簿可以有多个sheet
ExcelWriterSheetBuilder   构建一个 WriteSheet 对象  即一个工作表对象 对应excel 中的 sheet 页 ,一个工作簿可以有多个sheet
    
ReadListener 在每一行读取完毕后 都会调用 ReadListener 来处理数据,我们可以把调用service 的代码 写在invoke 中
WriteHandler  在每一个操作 包括创建单元格 创建表格 都会调用WriteHandler 来处理数据,对使用者透明不可见
所有的配置都是继承的,WorkBook 的配置会被sheet 继承,所以在EasyExcel 设置参数的时候  在EasyExcel . sheet()方法之前作用域是整个workbook的所有sheet,之后针对单个sheet

3.文件上传下载

3.1 上传文件

从 一个excel 文件中读取数据到数据库中

准备工作 搭建一个 ssm环境,或者 springmvc 的环境,咱们借助 springmvc 的 文件上传来做

添加依赖

shell
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>easyexcel</artifactId>
      <version>2.1.6</version>
    </dependency>
    
     <!--文件上传-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.4</version>
    </dependency>

配置springmvc文件

记得配置multipartResolver 这个 bean

准备监听器

java
package com.qf.ssm.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.qf.ssm.pojo.Student;
import com.qf.ssm.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * @ClassName : WebStudentListener
 * @Author : glls
 * @Date: 2021/8/23 16:04
 * @Description :
 */
@Component
@Scope("prototype")     // 注意 这里要设置成多例模式
public class WebStudentListener extends AnalysisEventListener<Student> {
    @Autowired
    StudentService studentService;

    @Override
    public void invoke(Student data, AnalysisContext context) {
        System.out.println(data);
        studentService.addStudent(data);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
		
    }
}

controller

java
 	  @Autowired
      WebStudentListener webStudentListener;
        @Autowired
        private StudentService studentService;
      
	  @RequestMapping("/read")
      @ResponseBody
      public String readExcel(MultipartFile file){
          
          try {
              ExcelReaderBuilder read = EasyExcel.read(file.getInputStream(), Student.class, webStudentListener);

              ExcelReaderSheetBuilder sheet = read.sheet();

              sheet.doRead();


          } catch (IOException e) {
              e.printStackTrace();
          }
          return "success";
      }

3.2 下载文件

把数据库中的数据下载到excel 中

controller

java
     @RequestMapping("/write")
    @ResponseBody
    public void writeExcel(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();
        
        ExcelWriterBuilder write = EasyExcel.write(outputStream,Student.class);

        ExcelWriterSheetBuilder sheet = write.sheet();
		
        //准备列表数据
        PageInfo<Student> pageData = studentService.getPageData(1, new Student());


        sheet.doWrite(pageData.getList());
    }

4.填充

Easyexcel 支持调整行高 列宽 背景色 字体大小等设置,但是 通过代码来控制比较麻烦,所以 不建议使用,但是 可以使用模板填充的方式 ,向预设样式的表格中直接写入数据,写入数据后 会保持原有样式

4.1 准备模板

Excel表格中用{}来包裹要填充的变量,如果单元格中本来就有 { } 左右大括号,需要在括号前面使用 \ 转义字符,

shell
\{  \}

代码中被填充数据的实体对象的成员变量名 或 被填充map 集合的 key 需要和excel 中 被{} 包裹的变量名称一致。

image-20210823171714934

4.2 封装数据

编写封装填充数据的类 或者 选择使用 map

java
@Test
    public void test03(){
        String template = "f://template1.xlsx";
        ExcelWriterBuilder write = EasyExcel.write("f://测试填充单一模板数据.xlsx", FillData.class);

        write.withTemplate(template);

        ExcelWriterSheetBuilder sheet = write.sheet();

        //准备实体类数据
        FillData fillData = new FillData();
        fillData.setName("zs");
        fillData.setAge(22);

        //准备map 数据
        HashMap<String, String> map = new HashMap<>();
        map.put("name","ls");
        map.put("age","21");


        //sheet.doFill(fillData);
        sheet.doFill(map);
    }

4.3 多组数据填充

和之前模板 不一样的地方 就是 多了个 .

image-20210823194449500

代码

java
 @Test
    public void test04(){
        String template = "f://template2.xlsx";
        ExcelWriterBuilder write = EasyExcel.write("f://测试填充多条数据模板.xlsx", FillData.class);

        write.withTemplate(template);

        ExcelWriterSheetBuilder sheet = write.sheet();

        List<FillData> fillData = Arrays.asList(new FillData("zs", 5), new FillData("ls", 6),new FillData("ww",7));

        sheet.doFill(fillData);
    }

4.4 组合数据填充

模板

image-20210823204841043

代码

java
 @Test
    public void test05(){
        //使用模板 组合填充    有多行 有单行
        String template = "f://template3.xlsx";
        ExcelWriter workBook = EasyExcel.write("f://测试填充组合数据模板.xlsx", FillData.class).withTemplate(template).build();


        WriteSheet writeSheet = EasyExcel.writerSheet().build();

        FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
        List<FillData> fillData = Arrays.asList(new FillData("zs", 5), new FillData("ls", 6),new FillData("ww",7));

        //填充并换行
        workBook.fill(fillData,fillConfig,writeSheet);  //填充多行数据

        //准备单行数据
        HashMap<String, String> map = new HashMap<>();
        map.put("date","1999-11-11");
        map.put("total","111");
        workBook.fill(map,writeSheet);  // 填充单行数据

        //关闭流
        workBook.finish();

    }

4.5 水平填充

模板

image-20210823205311596

java
 @Test
    public void test06(){
        //使用模板 水平填充
        String template = "f://template4.xlsx";
        ExcelWriter workBook = EasyExcel.write("f://测试水平填充数据模板.xlsx", FillData.class).withTemplate(template).build();


        WriteSheet writeSheet = EasyExcel.writerSheet().build();

        FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
        List<FillData> fillData = Arrays.asList(new FillData("zs", 5), new FillData("ls", 6),new FillData("ww",7));

        //填充并换行
        workBook.fill(fillData,fillConfig,writeSheet);  //填充多行数据

    

        //关闭流
        workBook.finish();
    }