首页 > 其他分享 >EasyExcel读取Excel数据(含多种方式)

EasyExcel读取Excel数据(含多种方式)

时间:2024-04-12 15:26:09浏览次数:24  
标签:读取 cachedDataList EasyExcel Excel private 监听器 public

目录

EasyExcel简介

使用EasyExcel进行读数据

引入依赖:

EasyExcel提供了两种读取模式

使用 监听器 读取模式

1.创建一个实体类

2.创建监听器

代码

使用 同步读 读取模式

1.创建一个实体类

2.代码

添加导入数据库的逻辑

其实官方文档讲得很清楚,可以看官方文档
官网:关于Easyexcel | Easy Excel (alibaba.com)

EasyExcel简介
简单地说,EasyExce是一个Java库,用于快速、简单地读写Excel文件

以下是官网的简介:
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进行读数据
引入依赖:

<!-- https://easyexcel.opensource.alibaba.com/docs/current/ -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.3.2</version>
        </dependency>

  

 

EasyExcel提供了两种读取模式
两种读取模式:

监听器:先创建监听器、在读取文件时绑定监听器。
单独抽离处理逻辑,代码清晰易于维护;一条一条处理,适用于数据量大的场景。

同步读:无需创建监听器,一次性获取完整数据。
方便简单,但是数据量大时会有等待时常,也可能内存溢出。

使用 监听器 读取模式
1.创建一个实体类
并使用 EasyExcel 提供的注解 @ExcelProperty 与 Excel 表对应(注意:需要实现 get 、set 方法,不然对象可能获取不到数据)

@Data
public class DemoData {
    /**
     * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
     */
    @ExcelProperty(index= 0)
    private Long planetCode;
    /**
     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
     */
    @ExcelProperty(value = "成员昵称")
    private String username;
    @ExcelProperty(value = "本月积分")
    private Double integral;
}

  

这里使用到了lombok中的@Data注解

@Data注解的类,编译后会自动给我们加上下列方法:

  • 所有属性的get和set方法
  • toString 方法
  • hashCode方法
  • equals方法
2.创建监听器
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {
 
    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
 
 
    public DemoDataListener() {
 
    }
 
 
 
    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        log.info("解析到一条数据:{}", new Gson().toJson(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() {
 
    }
}

   这里要引入gson

<dependency>
   <groupId>com.google.code.gson</groupId>
   <artifactId>gson</artifactId>
</dependency>

  代码

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.util.ListUtils;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
 
 
import java.util.List;
 
@Slf4j
public class ImportExcel {
    /**
     * 最简单的读
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
     * <p>
     * 3. 直接读即可
     */
    @Test
    public void simpleRead() {
        log.info("==========================写法1 不需要创建监听器===========================");
        // 写法1:JDK8+ ,不用额外写一个DemoDataListener
        // since: 3.0.0-beta1
        //3.0.0版本之后,使用这种方法不需要创建监听器
        String fileName = "D:\\testExcel.xlsx";
        // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
        // 具体需要返回多少行可以在`PageReadListener`的构造函数设置
        EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> {
            for (DemoData demoData : dataList) {
                log.info("读取到一条数据{}", new Gson().toJson(demoData));
            }
        })).sheet().doRead();
 
 
        log.info("==========================写法2 匿名内部类===========================");
        // 写法2:
        // 匿名内部类(创建一个监听器对象) 不用额外写一个DemoDataListener
        fileName = "D:\\testExcel.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, DemoData.class, new ReadListener<DemoData>() {
            /**
             * 单次缓存的数据量
             */
            public static final int BATCH_COUNT = 100;
            /**
             *临时存储
             */
            private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
 
            @Override
            public void invoke(DemoData data, AnalysisContext context) {
                cachedDataList.add(data);
                if (cachedDataList.size() >= BATCH_COUNT) {
                    saveData();
                    // 存储完成清理 list
                    cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                }
            }
 
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                saveData();
            }
 
            /**
             * 加上存储数据库
             */
            private void saveData() {
                log.info("{}条数据,开始存储数据库!", cachedDataList.size());
                log.info("存储数据库成功!");
            }
        }).sheet().doRead();
 
 
        log.info("==========================写法3 需创建监听器===========================");
        // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
        // 写法3:
        //这种方法需要写一个监听器
        fileName = "D:\\testExcel.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
 
 
        log.info("==========================写法4 读单文件多个sheet===========================");
        // 写法4
        //这种方法可以读一个文件里的多个sheet
        fileName = "D:\\testExcel.xlsx";
        // 一个文件一个reader
        try (ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build()) {
            // 构建一个sheet 这里可以指定名字或者no
            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            // 读取一个sheet
            excelReader.read(readSheet);
        }
 
 
        
    }
}

  

使用 同步读 读取模式
 1.创建一个实体类

并使用 EasyExcel 提供的注解 @ExcelProperty 与 Excel 表对应(注意:需要实现 get 、set 方法,不然对象可能获取不到数据)

@Data
public class DemoData {
    /**
     * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
     */
    @ExcelProperty(index= 0)
    private Long planetCode;
    /**
     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
     */
    @ExcelProperty(value = "成员昵称")
    private String username;
    @ExcelProperty(value = "本月积分")
    private Double integral;
}

  2.代码

/**    
     * 同步的返回,不推荐使用,如果数据量大会把数据放到内存里面
     */
    @Test
    public void synchronousRead() {
        String fileName = "D:\\testExcel.xlsx";
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 同步读取会自动finish
        List<DemoData> list = EasyExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
        for (DemoData data : list) {
            log.info("读取到数据:{}", new Gson().toJson(data));
        }
        // 这里 也可以不指定class,返回一个list,然后读取第一个sheet 同步读取会自动finish
        List<Map<Integer, String>> listMap = EasyExcel.read(fileName).sheet().doReadSync();
        for (Map<Integer, String> data : listMap) {
            // 返回每条数据的键值对 表示所在的列 和所在列的值
            log.info("读取到数据:{}",  new Gson().toJson(data));
        }
    }

  

添加导入数据库的逻辑

如果需要将导入的数据添加到数据库,看官网读Excel | Easy Excel (alibaba.com)

以下只给出部分示例

创建监听器:

// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {
 
    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private DemoDAO demoDAO;
 
    public DemoDataListener() {
        // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
        demoDAO = new DemoDAO();
    }
 
    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param demoDAO
     */
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }
 
    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(DemoData 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());
        demoDAO.save(cachedDataList);
        log.info("存储数据库成功!");
    }
}

  持久层:

/**
 * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
 **/
public class DemoDAO {
    public void save(List<DemoData> list) {
        // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
    }
}

  

 

 


原文链接:https://blog.csdn.net/m0_59084856/article/details/135024825

标签:读取,cachedDataList,EasyExcel,Excel,private,监听器,public
From: https://www.cnblogs.com/xianz666/p/18131332

相关文章

  • python操作Excel
    windows/linux安装openpyxlpipinstallopenpyxl 查找某Excel中是否存在某字符串text#输入:#file_path:Excel文件名#search_text:要查找的字符串#输出:#查到:[sheetname,cell.row,cell.column]组成的数组#未查到:Nonedeffind_text_in_ex......
  • json list to excel
    每次从数据库导出数据交付产品的时候常需要把json拍成excel"""json_to_excel.py~~~~~~~json文件转换为excel文件(xlsx)注:最多容纳1048576行数据"""importosimportsysimportjsonimportopenpyxlfromopenpyxl.stylesimportNamedStyle,Font,A......
  • Java从外部配置文件读取参数
    1.pom.xml<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://mav......
  • 多级动态表头导出-easyexcel
    导出如下动态表头 主要的构造tabCols和tableData,注意表头的字段,基本构造出了该格式所有的都能适配@GetMapping("/exportData")publicvoidexcelExport(TbDtTargetHealthMontbDtTargetHealthMon,HttpServletResponseresponse)throwsIOException{re......
  • pageoffice给在线打开的excel单元格插入图片
    转载:单元格添加图片#单元格添加图片查看本示例演示效果本示例关键代码的编写位置Vue+Springboot注意本文中展示的代码均为关键代码,复制粘贴到您的项目中,按照实际的情况,例如文档路径,用户名等做适当修改即可使用。Java命名空间com.zhuozhengsoft.pageoffice.excelwriter中的......
  • 继上期讲述MATLAB如何读取音频文件,这期讲述如何实现播放读取的音频文件并可以随时停止
    1.右击上期保存的GUI界面(.fig格式文件),点击“在GUIDE中打开“,如下图所示:2.然后按照上期绘制按钮操作,绘制出下图所示的GUI界面:3.再分别右击播放音频按钮和停止播放按钮,按照上期教学,输入各按钮所相对应的代码 ,播放音频按钮的代码是:globalyfs%定义全局变量sound(y,fs);......
  • Java程序中两种配置文件(xml和properties)的加载读取方法
    ​ Java程序中,经常需要从配置文件中加载并读取设置,以支持不同的配置环境和参数。最常用的配置文件格式是XML和properties。两种方法都非常基础,适合于简单的配置文件读取需求。对于更复杂的需求,可能需要更高级的解析技术或第三方库。参考文档:Java程序中两种配置文件(xml和prope......
  • WPF 读取和存储RichTextBox的文档内容
    在编辑RichTextBox内容时,我们看不到其文档的源码内容,因为我们没有像在Web开发中那样有浏览器自带的翻译功能可以使用(相关内容http://blog.sina.com.cn/s/blog_685790700100l61i.html)。将会用到两个对象 System.Windows.Markup命名空间下的XamlWriter对象和XamlReader对象。htt......
  • 【转载】[Excel] Excel打开第二个文件很慢的解决方法
    问题描述        该问题具体表现为:当打开第一个Excel文件后,在不关闭它的情况下接着打开第二个Excel文件,第二个Excel文件会延迟几秒之后才会正常打开。        注意,前提是第一个Excel文件打开速度是正常的,否则本解决方案大概率对你无效。    我的环境......
  • Rust 标准库 API 文件和文件夹操作 File,读取/创建/修改/追加/删除/重命名文件等
    File::create使用File的关联函数(类似Java中的静态方法)create,创建文件,如果存在,则覆盖。usestd::fs::{File,Metadata};fnmain()->std::io::Result<()>{letfile:File=File::create("foo.txt")?;letmetadata:Metadata=file.metadata()?;println!......