Spring Boot + EasyExcel实现Excel文件导入导出

news/2024/7/21 6:29:31 标签: spring boot, excel, 后端

Java解析、生成Excel比较有名的框架有Apache poi、jxl等,使用者可自行斟酌。

一、 为什么使用 EasyExcel

1.1 内存控制

Apache poi、jxl也能解析Excel,但他们都存在一个问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。

easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。

1.2 使用简洁

EasyExcel 可以映射excel和实体类,让代码变的更加简洁,读写更方便。

二、springboot 整合 EasyExcel

2.1 添加maven依赖
<!-- spring-boot 使用自己项目的版本就好 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- alibaba 相关依赖 -->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>3.3.3</version>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
</dependency>
<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency>
2.2 读Excel
2.2.1 准备数据源,Excel 表
姓名证件号证件类型
张三500000000000000000x户口簿
李四5111111111111111111身份证
王不二5222222222222222222身份证
赵五5252513xdg护照
2.2.2 创建与数据表对应的实体类
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author lqf
 * @date 2024/1/11 11:00
 */
@Data
@EqualsAndHashCode
public class AccountCenterByExcelVO {
        /**
     * 姓名
     */
    // 可以使用指定 表格下标或者 表头名字 的方式来对应数据。视情况选择,优弊如下:
    // 使用表下标,会让制表变得不灵活,使用表名会造成 名字重复,只有一个字段读取到数据
    // @ExcelProperty(index = 1)
    @ExcelProperty("姓名")
    private String name;

    /**
     * 证件号
     */
    @ExcelProperty("证件号")
    private String IdNumber;

    /**
     * 证件类型
     */
    @ExcelProperty("证件类型")
    private String IdType;


    /**
     * 这里用string 去接日期才能格式化。我想接收年月日格式
     */
    @ExcelProperty("日期")
    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
    private String date;

    /**
     * 想接收百分比的数字
     */
    @ExcelProperty("余额")
    @NumberFormat("#.##%")
    private String doubleData;
}
2.2.3 自定义转换器(可以不自定义,可使用EasyExcel 自带的 PageReadListener)
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.insupro.flexibleServerJ.dto.resp.vo.AccountCenterByExcelVO;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
 * @author lqf
 */
@Slf4j
public class ACByExcelListener implements ReadListener<AccountCenterByExcelVO> {

    /**
     * 每隔 N 条 进行一次数据处理,根据实际使用情况处理,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<AccountCenterByExcelVO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。
     * 用来存储或者处理数据
     */
    private AccountCenterByExcelVO excelVo;

    /**
     * 每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param excelVo
     */
    public ACByExcelListener(AccountCenterByExcelVO excelVo) {
        this.excelVo = excelVo;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data
     * @param context
     */
    @Override
    public void invoke(AccountCenterByExcelVO data, AnalysisContext context) {
        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("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    public void saveData() {
        log.info("{}条数据,开始处理数据!", cachedDataList.size());
        log.info("解析结果:"+JSON.toJSONString(cachedDataList));

        // 调用业务逻辑服务处理数据,实例是假装有业务逻辑
        log.info(excelVo.toString());

        log.info("数据处理成功!");
    }
}
2.2.4 创建一个文件处理 Controller

只是示例代码,业务逻辑没有写入server 层,使用的时候自行将业务代码归类


import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.fastjson.JSON;
import com.insupro.flexibleServerJ.dto.resp.vo.AccountCenterByExcelVO;
import com.insupro.flexibleServerJ.utils.ACByExcelListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.List;

/**
 * @author lqf
 * @date 2024/1/11 13:46
 */
@Slf4j
@RestController
@RequestMapping("api/easyExcel")
public class EasyExcelController {

    @RequestMapping("upExcel")
    public String upExcel(MultipartFile multipartFile){
        try {
            List<AccountCenterByExcelVO> acvoList = new ArrayList<>();
            // 第一种 使用 EasyExcel 自带的 PageReadListener,可以将数据读取后拿到读取的所有数据
            // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
            // 具体需要返回多少行可以在`PageReadListener`的构造函数设置
            EasyExcel.read(multipartFile.getInputStream(), AccountCenterByExcelVO.class,
                    new PageReadListener<AccountCenterByExcelVO>(dateList ->{
                        // 这里默认每次会读取100条数据,然后进入下一次读取
                        log.info(JSON.toJSONString(dateList));
                        // 保存每一次的数据读取即可
                        acvoList.addAll(dateList);
                    }))
                    .sheet()
                    .doRead();
            System.out.println(acvoList);

            // 第二种,使用自定义监听器, 数据集处理需要再监听器中定义
            EasyExcel.read(multipartFile.getInputStream(), AccountCenterByExcelVO.class,
                            new ACByExcelListener(new AccountCenterByExcelVO()))
                    .sheet()
                    // 不添加属性,默认头部是1行
                    .headRowNumber(1)
                    .doRead();
        }catch (Exception e){
            e.printStackTrace();
        }
        return "完成";
    }
}

excel_242">2.3 写出excel
2.3.1 创建写出的 实体类

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.Date;

/**
 * @author lqf
 * @date 2024/1/11 11:00
 */
@Data
@EqualsAndHashCode
public class AccountWriteForExcelVO {
    /**
     * 姓名
     * */
    // 可以使用指定 表格下标或者 表头名字 的方式来对应数据。视情况选择,优弊如下:
    // 使用表下标,会让制表变得不灵活,使用表名会造成 名字重复,只有一个字段读取到数据
    // @ExcelProperty(index = 1)
    @ExcelProperty("姓名")
    private String name;

    /**
     * 证件号
     */
    @ExcelProperty("证件号")
    private String IdNumber;

    /**
     * 证件类型
     */
    @ExcelProperty("证件类型")
    private String IdType;


    /**
     * 这里用string 去接日期才能格式化。我想接收年月日格式
     */
    @ExcelProperty("日期")
    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
    private Date date;

    /**
     * 想接收百分比的数字
     */
    @ExcelProperty("余额")
    @NumberFormat("#.##%")
    private Double doubleData;
}
2.3.2 实现简单写出逻辑
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import vo.AccountWriteForExcelVO;

import java.util.Date;
import java.util.List;

/**
 * @author lqf
 * @date 2024/1/11 11:00
 */
public class WriteExcleDemo {
    public static void main(String[] args) {
        // 使用自己指定输出的文件路径
        String fileName = "D:/workSpace/" + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        System.out.println(fileName);

        // 注意 数据量大参照 重复多次写入
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(fileName, AccountWriteForExcelVO.class)
                .sheet("模板")
                .doWrite(() -> {
                    // 分页查询数据
                    return data();
                });

        // 写法2
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(fileName, AccountWriteForExcelVO.class).sheet("模板").doWrite(data());

        // 写法3
        // 这里 需要指定写用哪个class去写
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, AccountWriteForExcelVO.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(data(), writeSheet);
        }
    }

    private static List<AccountWriteForExcelVO> data() {
        List<AccountWriteForExcelVO> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            AccountWriteForExcelVO data = new AccountWriteForExcelVO();
            data.setName("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }
}

结语

更多使用方法,见官方文档
https://easyexcel.opensource.alibaba.com/docs/current/


http://www.niftyadmin.cn/n/5349504.html

相关文章

【STM32】STM32学习笔记-Unix时间戳(41)

00. 目录 文章目录 00. 目录01. Unix时间戳02. UTC/GMT03. 时间戳转换04. C 标准库 <time.h>05. 时间相关函数示例5.1 time函数5.2 gmtime函数5.3 localtime函数5.4 mktime函数5.5 ctime函数5.6 asctime函数5.7 strftime函数 06. 预留07. 附录 01. Unix时间戳 •Unix 时…

首页图片自适应宣传海报

前言&#xff1a;昨天没有退款给别人&#xff0c;被人举报了&#xff0c;商户号封了&#xff0c;所以上面通知要做一个通知海报&#xff0c;告诉用户我们要升级对接第三方银联支付。我也不清楚怎么想的&#xff0c;有设计也不让设计处理图片&#xff0c;告诉我以后图片可能会奇…

Vue+OpenLayers7入门到实战:OpenLayers地图默认使用什么投影? 要如何更改OpenLayers地图的投影?

返回目录:Vue+OpenLayers7 OpenLayers地图默认使用什么投影? 在回答这个问题之前,我们需要了解什么是地图投影。 什么是地图投影? 地图投影是将球面地图上的三维地理坐标系(经纬度)转换成平面地图上的二维坐标系的过程。由于地球是一个近似的椭球体,而平面地图是一个二…

Spring Boot整合Mybatis配置多数据源

Spring Boot整合Mybatis配置多数据源 前言一、固定数据源配置二、动态数据源搞定收工&#xff01; 前言 在之前的事件管理系统博客中有提到动态的多数据源配置 工作中难免需要做几个工具方便自己偷懒&#xff0c;加上之前的挡板&#xff0c;数据源肯定没法单一配置&#xff0…

ReactHooks 官网文档翻译

useCallback(fn, dependencies) useCallback是一个React Hook&#xff0c;它允许您在重新渲染之间缓存函数定义。 const cachedFn useCallback(fn, dependencies)1、参数&#xff1a; fn&#xff1a;要缓存的函数值。它可以接受任何参数并返回任何值。React将在初始渲染期间…

详细分析SpringSecurity中的@PreAuthorize注解

目录 1. 基本知识2. 使用方式2.1 配置类2.2 直接使用 1. 基本知识 在Java中&#xff0c;PreAuthorize 是Spring Security框架中的一个注解&#xff0c;用于在方法调用之前对用户的权限进行验证。 允许在方法级别定义访问控制规则&#xff0c;确保只有满足指定条件的用户才能调…

Biu~笔记:高通蓝牙ADK(44)-- 32k cvc usb voice

Bui~ 在前面的博文中有提到&#xff0c;高通带来了aptx voice这个新功能。这是一种能传输32k 采样率的通话压缩格式&#xff0c;能让蓝牙做到更高的通话音质。之前我们都是用16k和8k的cvc&#xff0c;而aptx voice是32k的采样率&#xff0c;自然要用32k的cvc去处理&#xff0c;…

2024年人工智能产业十大发展趋势

2024年人工智能产业十大发展趋势 技术变革1. 多模态预训练大模型将是人工智能产业的标配2. 高质量数据愈发稀缺将倒逼数据智能飞跃3. 智能算力无处不在的计算新范式加速实现 应用创新4. 人工智能生成内容&#xff08;AIGC&#xff09;应用向全场景渗透5. 人工智能驱动科学研究&…