使用 EasyExcel 读取和下载 excel 文件

2023-10-30

前言

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称,EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。

构建工具类

@Data
public class User{

    @ExcelProperty(value = "名称", index = 0)
    @ColumnWidth(value = 15)
    private String name;//名称

    @ExcelProperty(value = "年龄", index = 1)
    @ColumnWidth(value = 15)
    private String age;//患者姓名

    @ExcelProperty(value = "性别", index = 2)
    @ColumnWidth(value = 15)
    private String sex;//患者id

    @ExcelProperty(value = "创建时间", index = 4)
    @ColumnWidth(value = 15)
    private String confirmDate;//创建时间
}

读取excel文件

读取excle文件可以分为两种方式,一种是异步读取,另一种是同步读取

异步

异步读取首先要构建监听器继承AnalysisEventListener

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;

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

public class ExcelListener<T> extends AnalysisEventListener<T> {

    private List<T> list=new ArrayList<>();

    //读取每一行数据执行一次
    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        list.add(t);
    }
	//读取表头内容
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        System.out.println("表头:"+headMap);
    }
	//数据全部读取完毕
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    	save(list);
        System.out.println("所有数据解析完毕.................");
    }
    public void save(List<T> list){
		//逻辑代码
	}
}

读取代码

public class ExcelUtils {
/**
     * 读取excel文件
     * @param file 文件
     * @return List
     */
    public static void readExcel(File file){
        InputStream ins=null;
        try {
            ins=new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //sheet 设置处理的工作簿 headRowNumber 设置从excel第几行开始读取
       EasyExcel.read(ins, User.class ,new ExcelListener<User>()).sheet().headRowNumber(1).doRead();//第0行一般是表头,从第1行开始读取
    }
}
public class Test{
	public static void main(String[] args) {
		ExcelUtils readExcel(new File("D:\\test.xlsx"),User.class);
	}
}
同步

同步读取可以不需要上面的监听器了(不过如果需要验证表头的话,还是可以加上监听器来验证表头信息)
其实也就是 一个使用 doRead() 另一个使用 doReadSync()

public class ExcelUtils {
    /**
     * 读取excel文件
     * @param file 文件
     * @return List
     */
    public static List readExcel(File file,Class clazz){
        InputStream ins=null;
        try {
            ins=new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //sheet 设置处理的工作簿 headRowNumber 设置从excel第几行开始读取
        return EasyExcel.read(ins).head(clazz).sheet().headRowNumber(1).doReadSync();//第0行一般是表头,从第1行开始读取
    }

一些读取数据可能会碰到的小问题

1.在exlcel中有明明有数据,但是读取进来后对象属性全是空

有可能是使用了 注解 @Accessors(chain = true) 这个注解会和 EasyExcel 有冲突,导致数据无法读取进来,一些高版本好像已经修复了此BUG

2.在读取日期形式的数据时,可能会造成精度丢失 比如:

在这里插入图片描述
这样的 yyyy-MM-dd HH:mm:ss 数据读取进来后可能会 变成 yyyy-MM-dd HH:mm

也就是这样
在这里插入图片描述
这种情况可以在实体工具类中增加一个注解,来让EasyExcel 知道读取日期的格式

	@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
    @ExcelProperty(value = "创建时间", index = 4)
    @ColumnWidth(value = 15)
    private String confirmDate;//创建时间

这样读取进来的日期就会正常了

3.在目前的 easyExcel 中有一些 BUG ,如果 excel 第一行没有内容,是不会触发 invokeHeadMap 的方法的
也就是说如果你传入的 excel 文件第一行没有内容,那么你写在 invokeHeadMap 方法中的表头验证方法是不会被触发的

目前有一个办法是在 invoke 中写一些代码进行判断 如下:

 	private boolean isModel=true;
	 @Override
    public void invoke(Object o, AnalysisContext analysisContext) {
        //非正常表格式验证,第一行没数据
        if(isModel){
            throw new SystemException("请使用规定模板查询");
        }
    }
    
    @Override
    public void invokeHeadMap(Map headMap, AnalysisContext context) {
        if(isModel){
            isModel=false;
        }
        ..........//其他验证表头代码
	}

导出excel文件

向浏览器输出excel文件
public class ExcelUtils {
	/**
     * 像浏览器输出excel文件
     * @param response  HttpServletResponse
     * @param data  输出的数据
     * @param fileName 输出的文件名称 excel的名称
     * @param sheetName  输出的excel的sheet的名称 也就是页的名称
     * @param clazz  输出数据的模板
     */
    public static void  writeExcel(HttpServletResponse response, List<? extends Object> data, String fileName, String sheetName, Class clazz){
        //表头样式
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //设置表头居中对齐
        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //内容样式
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //设置内容靠左对齐
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
        try {
            EasyExcel.write(getOutputStream(fileName, response), clazz).excelType(ExcelTypeEnum.XLSX).sheet(sheetName).registerWriteHandler(horizontalCellStyleStrategy).doWrite(data);
        } catch (Exception e) {
            throw new SystemException("输出excel文件失败", e);
        }

    }

    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
        return response.getOutputStream();
    }
}
@GetMapping("/test/downloadExcel")
public static void test(){
	List<User> userList=new Arraylist<>();
	//省略添加数据代码
	try {
         ExcelUtil.writeExcel(response, userList, fileName, sheetName, UserExcel.class);
    } catch (Exception e) {
           e.printStackTrace();
    }
}
读取模版,向指定位置输出excel文件
public class ExcelUtils {
    /*
     * @Author: pang.jialei
     * @Description: 读取excle模板,将数据输出到模板
     * @Date: 2021/6/3 15:04
     * @Param: templateInputStream: 模板所在的地址
     * @param tempFileName: 生成的excel文件名称
     * @param downloadDir: 输出excel文件的地址
     * @param parms: list表格数据
     * @param mapData: 其他参数
     **/
    public static void invoke(InputStream templateInputStream, String tempFileName, String downloadDir, List<?> parms, Map<String, Object> mapData) {
        File file = new File(downloadDir);
        if (!file.exists()) {
            file.mkdirs();
        }
        String tempFile = downloadDir + File.separator + tempFileName;

        ExcelWriter excelWriter = EasyExcel.write(tempFile).withTemplate(templateInputStream).build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
        // 如果数据量大 list不是最后一行 参照下一个
        if (parms != null) {
            FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            excelWriter.fill(parms, fillConfig, writeSheet);
        }
        if (mapData != null) {
            excelWriter.fill(mapData, writeSheet);
        }
        excelWriter.finish();
    }
读取模版,向指定位置输出多sheet excel文件

    /*
     * @Author: pang.jialei
     * @Description: 读取excle模板,将数据输出到多sheet模板
     * @Date: 2021/6/3 15:04
     * @Param: templateInputStream: 模板所在的地址
     * @param tempFileName: 生成的excel文件名称
     * @param downloadDir: 输出excel文件的地址
     * @param listMapData: <sheet名称,list数据> 的表格数据
     * @param sheetNames: sheet名称
     * @param mapData: <sheet名称,其他参数<key,value>> 其他参数
     **/
    public static void invokeListSheet(InputStream templateInputStream, String tempFileName, String downloadDir, Map<String,List<?>> listMapData, List<String> sheetNames, Map<String,Map<String, Object>> mapData) {
        File file = new File(downloadDir);
        if (!file.exists()) {
            file.mkdirs();
        }
        String tempFile = downloadDir + File.separator + tempFileName;
        ExcelWriter excelWriter = EasyExcel.write(tempFile).withTemplate(templateInputStream).build();
        for (String sheet:sheetNames) {
            WriteSheet writeSheet = EasyExcel.writerSheet(sheet).build();
            // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
            // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
            // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置 forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
            // 如果数据量大 list不是最后一行 参照下一个
            List parms= listMapData.get(sheet);
            if (parms != null) {
                FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
                excelWriter.fill(parms, fillConfig, writeSheet);
            }
            if (mapData != null) {
                excelWriter.fill(mapData.get(sheet), writeSheet);
            }
        }
        excelWriter.finish();
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 EasyExcel 读取和下载 excel 文件 的相关文章

随机推荐

  • jQuery.validate.js API

    jQuery validate js API Name Type validate options Returns Validator 验证所选的FORM valid Returns Boolean 检查是否验证通过 rules Retur
  • C 输入 & 输出

    C 语言把所有的设备都当作文件 所以设备 比如显示器 被处理的方式与文件相同 以下三个文件会在程序执行时自动打开 以便访问键盘和屏幕 表1 标准文件 标准文件 文件指针 标准输入 stdin 标准输出 stdout 标准错误 stderr
  • JavaScript的常用继承方法

    原型链继承 让一个构造函数的原型 prototype 是另一个构造函数的实例 优点 写法方便简洁 容易理解 缺点 对象实例共享所有继承的属性和方法 不能传递参数 function Person age this name 张三 this a
  • Global对象的理解与认识

    JavaScript 中有一个特殊的对象 称为全局对象 Global Object 它及其所有属性都可以在程序的任何地方访问 即全局变量 在浏览器端的 JavaScript 中 通常 window 是全局对象 而 Node js 中的全局对
  • 【目标检测】31、Alpha-IoU: A Family of Power Intersection over Union Losses for Bounding Box Regression

    文章目录 一 背景 二 方法 2 1 Alpha IoU 的提出 2 2 Alpha IoU 的特性 三 效果 论文 Alpha IoU A Family of Power Intersection over Union Losses fo
  • CountDownLatch的两种常用场景

    CountDownLatch的两种使用场景 先来看看 CountDownLatch 的源码注释 A synchronization aid that allows one or more threads to wait until a se
  • Linux的环境变量

    Linux的环境变量 一 Linux的变量种类 按变量的生存周期来划分 Linux变量可分为两类 1 永久的 需要修改配置文件 变量永久生效 2 临时的 使用export命令声明即可 变量在关闭shell时失效 二 设置变量的三种方法 1
  • HTTP协议1)----对于应用层的详细讲解

    大家好 我是 兔7 一位努力学习C 的博主 如果文章知识点有错误的地方 请指正 和大家一起学习 一起进步 如有不懂 可以随时向我提问 我会全力讲解 如果感觉博主的文章还不错的话 希望大家关注 点赞 收藏三连支持一下博主哦 你们的支持是我创作
  • mos管的参数及选择(比较好) 并附三极管

    附1 三极管 要想使管子饱和导通 则应该 NPN型 Ub gt Ue Ub gt Uc PNP型 Ue gt Ub Uc gt Ub http jeanscheng blog 163 com blog m 0 t 1 c fks 08707
  • openwrt-安装图形界面

    一般网上下载的固件都安装有图形界面 不过有时候也会有特例 下面简要记录luci图形界面的安装过程 opkg update 更新软件库 opkg install luci opkg install luci i18n chinese 安装语言
  • vscode+wampserver编写php程序无法接收html提交的表单

    php程序打算获取来自html的用户名变量并打印 代码如下 欢迎 打开编写好的html网页 点击提交表单数据时 无法打开相应的php文件 查了很多资料也问了同学 都没找到原因 最后想起来我在vscode选择打开html网页的方式为 它会在一
  • QProgressBar详解

    QProgressBar详解 1 QProgressBar详解 1 QProgressBar简述 2 常用方法 3 示例 比较进度条 4 设置样式表 1 QProgressBar详解 原文链接 https blog csdn net wzz
  • K210学习笔记(二)——学习前准备

    一 开发板的选择 我现在手上有两块K210开发板 一块是亚博K210 还有一块是MAIX BIT 亚博K210使用的是C语言 用C语言速度快 但是学习难度大 而MAIX BIT使用的是MicroPython并且有配套的maixide 但是M
  • 数据结构模拟器

    数据结构模拟器推荐 https www cs usfca edu galles visualization Algorithms html
  • Java中捕获异常的灵魂四问

    1 如果try中没有遇到问题 怎么执行 只会执行try中的代码 不会执行catch 2 如果try中会遇到多个问题 我们应该怎么做 如果遇到多个异常 应该写多个catch与之对应 但是注意 catch要捕获的异常如果存在父子关系 那么要把父
  • HX711称重传感器

    HX711 称重传感器专用模拟 数字 A D 转换器芯片 简介 HX711是一款专为高精度称重传感器而设计的24位A D转换器芯片 与同类型其它芯片相比 该芯片集成了包括稳压电源 片内时钟振荡器等其它同类型芯片所需要的外围电路 具有集成度高
  • 解决wsl和vmware冲突问题

    CMD管理员模式启动 输入bcdedit set hypervisorlaunchtype auto开启 则可以使用WSL CMD管理员模式启动 输入bcdedit set hypervisorlaunchtype off关闭 则可以使用V
  • vue-amap 地图定位打卡

    1 注册并登录高德开放平台 1 首先 注册开发者账号 成为高德开放平台开发者 2 登陆之后 在进入 应用管理 页面 创建新应用 3 为应用添加 Key 4 添加成功后 可获取到key值和安全密钥jscode 自2021年12月02日升级 升
  • 71.编写input()和output()函数的输入,输出5个学生的数据记录

    include
  • 使用 EasyExcel 读取和下载 excel 文件

    前言 EasyExcel是阿里巴巴开源的一个excel处理框架 以使用简单 节省内存著称 EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中 而是从磁盘上一行行读取数据 逐个解析 Easy