Spring Boot集成EasyExcel实现excel导入导出操作


Easy Excel 官网

Spring Boot集成EasyExcel实现excel导入导出操作

0 简要说明

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。

在这里插入图片描述

简单使用

引入依赖

        <!--easyexcel-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.1</version>
        </dependency>

读操作

excel源文件

在这里插入图片描述

实体类

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

    private Long id;

    /**
     * 用户名
     */

    private String userName;
    /**
     * 生日
     */

    private Date birthday;
    /**
     * 性别
     */

    private String sex;
    /**
     * 地址
     */

    private String address;

}
监听器

在这里插入图片描述

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去

package com.geekmice.springbootselfexercise;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.geekmice.springbootselfexercise.dao.UserDao;
import com.geekmice.springbootselfexercise.domain.TempData;
import com.geekmice.springbootselfexercise.service.UserService;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * @BelongsProject: spring-boot-self-exercise
 * @BelongsPackage: com.geekmice.springbootselfexercise
 * @Author: pingmingbo
 * @CreateTime: 2023-08-07  10:19
 * @Description: easyexcel读操作监听器
 * @Version: 1.0
 */
@Slf4j
public class UserListener  implements ReadListener<TempData> {
    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<TempData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private UserService userService;

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
    public UserListener(UserService userService) {
        this.userService = userService;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(TempData data, AnalysisContext context) {
        log.info("解析到一条数据:{}", JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", cachedDataList.size());
        // this.s(cachedDataList);
        // userService.saveBatch(cachedDataList);
        log.info("存储数据库成功!");
    }
}
业务代码

//userService业务service,也可以是dao,如果是多个mapper或者service,通过构造方法或者set方法传递
也可以使用工具类getBean获取,不需要通过构造方法,这种方式主要是多个mapper或者service进行业务操作

  @Override
    public void uploadFileByEasyExcel(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), TempData.class, new UserListener(userService)).sheet().doRead();
        } catch (IOException e) {
            log.error("error msg 【{}】", e);
            throw new IllegalArgumentException(e);
        }
    }

写操作

实体类
名称默认值备注
value用于匹配excel中的头,必须全匹配,如果有多行头,会匹配最后一行头
orderInteger.MAX_VALUE优先级高于value,会根据order的顺序来匹配实体和excel中数据的顺序
index-1优先级高于valueorder,会根据index直接指定到excel中具体的哪一列
converter自动选择指定当前字段用什么转换器,默认会自动选择。读的情况下只要实现com.alibaba.excel.converters.Converter#convertToJavaData(com.alibaba.excel.converters.ReadConverterContext<?>) 方法即可

ExcelIgnore

默认所有字段都会和excel去匹配,加了这个注解会忽略该字段

ExcelIgnore

默认所有字段都会和excel去匹配,加了这个注解会忽略该字段

package com.geekmice.springbootselfexercise.domain;

import cn.afterturn.easypoi.excel.annotation.Excel;
import cn.afterturn.easypoi.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @BelongsProject: spring-boot-self-exercise
 * @BelongsPackage: com.geekmice.springbootselfexercise.domain
 * @Author: pingmingbo
 * @CreateTime: 2023-08-07  09:53
 * @Description: TODO
 * @Version: 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class TempData {
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private Long id;

    /**
     * 用户名
     */
    @ExcelProperty(value = "用户名")
    private String userName;
    /**
     * 生日
     */
    @ExcelProperty(value ="生日",format = "yyyy-MM-dd")
    private Date birthday;
    /**
     * 性别
     */
    @ExcelProperty(value ="性别")
    private String sex;
    /**
     * 地址
     */
    @ExcelProperty(value = "地址")
    private String address;

}
excel示例

在这里插入图片描述

业务代码
    @Override
    public void downloadFileByEasyExcel() {
         EasyExcel.write("D:\\easyexcel.xls", TempData.class)
                 .sheet()
                 .doWrite(data());
                     }
根据参数指定列导出

在这里插入图片描述

    /**
     * 根据参数指定列导出
     */
    @Test
    public void t2(){
        List<String> list = new ArrayList(16);
        list.add("sex");
        list.add("userName");
        EasyExcel.write("D://easyexcel_by_columns.xlsx", TempData.class)
                .excludeColumnFieldNames(null)
                .sheet()
                .includeColumnFieldNames(list)
                .doWrite(data());
        log.info("导出结束 [{}]", DateFormatUtils.format(new Date(), DateUtils.DATE_FORMAT_19));
    }
指定哪几列导出

使用index属性,index=2空余出来,这样一来第二列为空
在这里插入图片描述
在这里插入图片描述

复杂头导出

在这里插入图片描述
在这里插入图片描述

    /**
     * 用户名
     */
    @ExcelProperty(value = {"父级","用户名"},index = 0)
    private String userName;
    /**
     * 生日
     */
    @ExcelProperty(value ={"父级","生日"},format = "yyyy-MM-dd",index = 1)
    private Date birthday;
    /**
     * 性别
     */
    @ExcelProperty(value ={"父级","性别"},index = 2)
    private String sex;
    /**
  List<String> list = new ArrayList(16);
        list.add("sex");
        list.add("userName");
        list.add("birthday");
        EasyExcel.write("D://easyexcel_by_columns.xlsx", TempData.class)
                .excludeColumnFieldNames(null)
                .sheet()
                .includeColumnFieldNames(list)
                .doWrite(data());
        log.info("导出结束 [{}]", DateFormatUtils.format(new Date(), DateUtils.DATE_FORMAT_19));

关于数值型,日期型,浮点型数据解决方案

在这里插入图片描述

实体类接收字符串获取
package com.geekmice.springbootselfexercise.domain;

import cn.afterturn.easypoi.excel.annotation.Excel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.geekmice.springbootselfexercise.utils.CustomStringStringConverter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

/**
 * @BelongsProject: spring-boot-self-exercise
 * @BelongsPackage: com.geekmice.springbootselfexercise.domain
 * @Author: pingmingbo
 * @CreateTime: 2023-08-07  14:04
 * @Description: TODO
 * @Version: 1.0
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ConverterData {
    /**
     * 生日
     */
    @ExcelProperty(value = "生日")
    private String birthday;
    /**
     * 性别
     */
    @ExcelProperty(value = "性别")
    private String sex;

    /**
     * 分数
     */
    @ExcelProperty(value = "分数")
    private String score;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值