文章目录

  • 写在前面
  • 1、maven依赖
  • 2、导入Excel文件
  • 2.1、读取表格文件
  • 2.2、如果有多个sheet表格
  • 2.3、监听器封装(也可不封装)
  • 2.4、读取数据格式化(实体类中添加注解)
  • 3、导出Excel文件
  • 3.1、导出表格格式(实体类中添加注解)
  • 3.2、添加表头样式
  • 4、注意事项
  • 4.1、实体类字段set方法,不能返回this
  • 4.2、EasyExcel 和 EasyExcelFactory 的区别
  • 4.3、HSSFWorkbook、XSSFWorkbook、Workbook


写在前面

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。
github文档: https://github.com/alibaba/easyexcel

1、maven依赖

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

2、导入Excel文件

常用如下两种读取方式

EasyExcel.read(文件名/文件流,数据实体类.class,监听器).sheet().doRead();
EasyExcel.read(文件名/文件流,数据实体类.class,监听器).build();

2.1、读取表格文件

实例1:
(这里假设已有实体类User)

public void test1() {
    EasyExcel.read("D:\\file\\1.xlsx", User.class, new AnalysisEventListener<User>() {
        // 每解析一行数据,该方法会被调用一次
        @Override
        public void invoke(User user, AnalysisContext analysisContext) {
            System.out.println("解析数据为:" + user.toString());
        }
        
        // 全部解析完成被调用
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            System.out.println("解析完成...");
            // 保存到数据库
        }
    }).sheet().doRead();
}

实例2:spring项目中导入一个excel文件
(这里假设已有实体类User)

// springboot 接口
@PostMapping("/importUser")
public void importUser(@RequestParam("file") MultipartFile file) 
    ExcelReader excelReader = EasyExcel.read(file.getInputStream(), User.class, new AnalysisEventListener<DemoData>() {
        @Override
        public void invoke(User user, AnalysisContext analysisContext) {
            System.out.println("解析数据为:" + user.toString());
        }
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            System.out.println("解析完成...");
            // 保存到数据库
        }
    }).build();
    
    ReadSheet sheet = EasyExcel.readSheet(0).build();    // 创建sheet对象,读取Excel的第一个sheet, 也可以根据sheet名称获取
    excelReader.read(sheet);                             // 读取sheet表格数据,可以读取多个sheet
    excelReader.finish();                                // 这里必须手动关闭
}

2.2、如果有多个sheet表格

读取多个sheet表格

// 方式1
EasyExcel.read(...)
//         .sheet(0).doRead();
           .doReadAll(); // 读取全部sheet


// 方式2
ExcelReader excelReader = EasyExcel.read(...) .build();
ReadSheet sheet = EasyExcel.readSheet(0).build();
//excelReader.read(sheet);
excelReader.readAll(); // 读所有sheet
excelReader.finish();


// 读指定的多个sheet
ExcelReader excelReader = EasyExcel.read(...) .build();
ReadSheet sheet = EasyExcel.readSheet(0).build();
// 读取sheet,有几个就构建几个sheet进行读取
excelReader.read(sheet0);
excelReader.finish();

2.3、监听器封装(也可不封装)

每次调用 EasyExcel.read() 都需要 new 一个 AnalysisEventListener 对象,我们可以将AnalysisEventListener封装为一个对象,方便重复调用

public class EasyExcelUtils<T> {

    /**
     * 获取读取Excel的监听器对象
     * @param consumer 处理解析数据的函数, 一般可以是数据入库逻辑的函数
     * @param threshold 阈值,达到阈值就处理一次存储的数据
     * @param <T> 数据模型泛型
     * @return 返回监听器
     */
    public static <T> AnalysisEventListener<T> getReadListener(Consumer<List<T>> consumer, int threshold) {

        return new AnalysisEventListener<T>() {
        
           // ArrayList 查询更快
           // List<T> dataList = new ArrayList<>(threshold);
           // LinkedList 插入和删除更快
            List<T> dataList = new LinkedList<>(); 

            /**
             * 每解析一行调用, 订阅者1
             * @param data 解析的每行数据
             * @param context
             */
            @Override
            public void invoke(T data, AnalysisContext context) {
                dataList.add(data);
                // 达到阈值就处理一次存储的数据
                if (dataList.size() >= threshold) {
                    consumer.accept(dataList);
                    dataList.clear();
                }
            }

            /**
             * excel文件解析完成后,事件调度中心会通知到该方法, 订阅者2
             * @param context
             */
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                // 最后阈值外的数据做处理
                if (dataList.size() > 0) {
                    consumer.accept(dataList);
                }
            }
        };

    }

    /**
     * 获取读取Excel的监听器对象, 不指定阈值, 默认阈值为 2000
     */
    public static <T> AnalysisEventListener<T> getReadListener(Consumer<List<T>> consumer) {
        return getReadListener(consumer, 2000);
    }
}

使用示例

// 读取excel文件
public void test() {
    EasyExcel.read("user.xlsx", User.class,EasyExcelUtils.getReadListener(dataProcess())).doReadAll();
}

// 处理数据
public Consumer<List<User>> dataProcess() {
    return users -> users.forEach(System.out::println);
}

2.4、读取数据格式化(实体类中添加注解)

在实体类中加入注解可以格式化数据,
与lombok结合使用。

@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class User {

    @ExcelProperty(value = "字符串标题", index = 0)
    private String name;
    
    @ExcelProperty(value = "日期标题", index = 1)
    @DateTimeFormat(value = "yyyy年MM月dd日 HH时mm分ss秒")  // 格式化日期类型数据
    private Date hireDate;
    
    @ExcelProperty(value = "数字标题", index = 2)
    @NumberFormat(value = "###.#")  // 格式化数字类型数据,保留一位小数,@NumberFormat不能用在Double类型中
    private String salary;
}

3、导出Excel文件

// 1、根据实体对象导出文件
EasyExcel.write(response.getOutputStream(), 实体.class).sheet().doWrite(list<实体>);

// 2、根据List<List<String>>导出文件
EasyExcel.write(response.getOutputStream()).head(表头list对象).sheet().doWrite(数据list对象);

// 3、根据模板文件
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(new ClassPathResource("模板.xlsx").getInputStream()).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
excelWriter.fill(list实体对象, writeSheet);  // 填充数据1
excelWriter.fill(map对象, writeSheet);       // 填充数据2
excelWriter.finish();                        // 关闭流

实例:spring项目中导出excel文件

@PostMapping("/getExcel")
public void getExcel(HttpServletResponse response) 
	try{
		String fileName = "表格文件名.xlsx";
		List<数据实体类> data = new ArrayList<>();
		fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString());
		response.addHeader("Content-Disposition", "attachment;filename=" + fileName);
		EasyExcelFactory.write(response.getOutputStream(), 数据实体类.class)
			.sheet("这是一张表名")
			.doWrite(data);
		response.getOutputStream().flush();
	}finally {
		response.getOutputStream().close();
	}
}

3.1、导出表格格式(实体类中添加注解)

在实体类中定义

@Data
@NoArgsConstructor
//@ContentRowHeight(50)//内容单元格高度
//@HeadRowHeight(50)//表头单元格高度
//@ColumnWidth(50)//单元格宽度
public class User {
	
    @ExcelProperty(value = {"导出台账","序号"})
    private Integer no;
    
    @ExcelProperty(value = {"导出台账","编号"})
    private  String  code;
}

3.2、添加表头样式

EasyExcelFactory.write(...).sheet("表名").registerWriteHandler(new 表头样式对象());

表头样式类型必须继承自CellWriteHandler

public class MyCellWriteHandler implements CellWriteHandler {...}

4、注意事项

4.1、实体类字段set方法,不能返回this

实体类字段set方法,不能返回this,否者读表格时,无法写入数据到实体类。
也意味着实体类不能添加链式相关注解,例如:lombok的@Accessors(chain = true)

4.2、EasyExcel 和 EasyExcelFactory 的区别

EasyExcel.read(…) 和 EasyExcelFactory.read(…) 有区别吗?
没区别。

下面是EasyExcel的源码

package com.alibaba.excel;

public class EasyExcel extends EasyExcelFactory {
    public EasyExcel() {
    }
}

你没有看错,EasyExcel.java的源码就这么点

4.3、HSSFWorkbook、XSSFWorkbook、Workbook

Workbook 是 HSSFWorkbook、XSSFWorkbook的基类

HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls

XSSFWorkbook:是操作Excel2007的版本,扩展名是.xlsx