数据导入导出(EasyExcel)框架入门指南

写在前面
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站


文章目录

  • EasyExcel 框架
    • 概述
    • 依赖
    • API
      • Excel 实体类注解
      • 写 Excel
        • 概念介绍
        • 写 Excel 通用参数
        • WriteWorkbook
        • WriteSheet
        • WriteTable
  • 代码示例
    • 创建 Excel 文件
      • (1)配置实体类
      • (2)Excel 文件的生成和下载
    • 模板打印
      • (1)定义模板
      • (2)填充模板并下载 Excel 模板文件
    • 解析 Excel 文件
      • (1)配置实体类
      • (2)解析 Excel 文件
  • 拓展
    • csv 读写的支持

EasyExcel 框架

本文仅作为快速入门指南,深入学习详见官方文档

概述

Java 解析、生成 Excel 比较有名的框架有Apache poi、jxl,但它们都存在一些严重的问题,如代码编写繁琐且重复,极其耗费内存。EasyExcel 是一个基于 Java 的简单、省内存的读写 Excel 的开源项目。在尽可能节约内存的情况下支持读写百万级数据的 Excel。

EasyExcel:阿里巴巴开源的操作 Excel 的框架,底层封装的 POI,可以大大减少占用内存的主要原因是在解析 Excel 时,没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

下图是 easyExcel 和 POI 在解析 Excel 时的对比图:

在这里插入图片描述


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

在这里插入图片描述

  • 在实体类上配置注解(适配 Excel 中的标题)
  • 调用 EasyExcel 的工具类方法,完成文件的上传或者下载

依赖

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

API

Excel 实体类注解

  • @ExcelIgnoreUnannotated:标注在实体类上,作用是只使用标注了 @ExcelProperty 注解的字段参与 excel 读写。默认是不管加不加 ExcelProperty 的注解,所有字段都会参与读写

  • @ExcelProperty:标注在实体类属性上,用于 excel 的列和实体类属性的匹配

    参数如下:

    名称默认值描述
    value用于匹配excel中的头,必须全匹配,如果有多行头,会匹配最后一行头
    orderInteger.MAX_VALUE优先级高于value,会根据order的顺序来匹配实体和 excel 中数据的顺序
    index-1优先级高于valueorder,会根据index直接指定到excel中具体的哪一列
    converter自动选择指定当前字段用什么转换器,默认会自动选择。写的情况下只要实现com.alibaba.excel.converters.Converter#convertToExcelData(com.alibaba.excel.converters.WriteConverterContext<T>) 方法即可
  • @ExcelIgnore:标注在实体类属性上,默认所有字段都会和 excel 去匹配,加了这个注解会忽略该字段

  • @DateTimeFormat:标注在实体类属性上,日期转换

    参数如下:

    名称默认值描述
    value参照java.text.SimpleDateFormat书写即可
    use1904windowing自动选择excel中时间是存储1900年起的一个双精度浮点数,但是有时候默认开始日期是1904,所以设置这个值改成默认1904年开始
  • @NumberFormat:标注在实体类属性上,数字转换

    参数如下:

    名称默认值描述
    value参照java.text.DecimalFormat书写即可
    roundingModeRoundingMode.HALF_UP格式化的时候设置舍入模式

写 Excel

概念介绍
  • WriteWorkbook :可以理解成一个 excel 文件
  • WriteSheet :理解成一个excel 文件里面的一个表单
  • WriteTable : 一个表单里面如果有多个实际用的表格,则可以用 WriteTablejava

写 Excel 通用参数

WriteWorkbook,WriteSheet ,WriteTable 都会有的参数,如果为空,默认使用上级。

名称默认值描述
converter默认加载了很多转换器,这里可以加入不支持的字段
writeHandler写的处理器。可以实现WorkbookWriteHandler,SheetWriteHandler,RowWriteHandler,CellWriteHandler,在写入excel的不同阶段会调用
relativeHeadRowIndex0写入到excel和上面空开几行
headclazz二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class
clazzhead二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据
autoTrimtrue会对头、读取数据等进行自动trim
use1904windowingfalseexcel中时间是存储1900年起的一个双精度浮点数,但是有时候默认开始日期是1904,所以设置这个值改成默认1904年开始
useScientificFormatfalse数字转文本的时候在较大的数值的是否是否采用科学计数法
needHeadtrue是否需要写入头到excel
useDefaultStyletrue是否使用默认的样式
automaticMergeHeadtrue自动合并头,头中相同的字段上下左右都会去尝试匹配
excludeColumnIndexes需要排除对象中的index的数据
excludeColumnFieldNames需要排除对象中的字段的数据
includeColumnIndexes只要导出对象中的index的数据
includeColumnFieldNames只要导出对象中的字段的数据

WriteWorkbook

设置方法如下(找不到参数的看下通用参数里面是否存在):

EasyExcel.write(fileName, DemoData.class)
    // 在 write 方法之后, 在 sheet方法之前都是设置 WriteWorkbook 的参数
    .sheet("模板")
    .doWrite(() -> {
        // 分页查询数据
        return data();
    });

WriteWorkbook 方法:

public static ExcelWriterBuilder write()
public static ExcelWriterBuilder write(File file)
public static ExcelWriterBuilder write(String pathName)
public static ExcelWriterBuilder write(String pathName, Class head)
public static ExcelWriterBuilder write(OutputStream outputStream)
public static ExcelWriterBuilder write(OutputStream outputStream, Class head)
// 参数说明:
// 		file		创建的excel文件路径,路径需包含文件名
// 		pathName	创建的excel文件路径,路径需包含文件名
// 		outputStream	创建的excel文件的输出流,路径需包含文件名
// 		head		excel文件的行头实体类(标题栏)

WriteWorkbook 参数:

名称默认值描述
excelType当前excel的类型,支持XLS、XLSX、CSV
outputStreamfile二选一。写入文件的流
fileoutputStream二选一。写入的文件
templateInputStream模板的文件流
templateFile模板文件
charsetCharset#defaultCharset只有csv文件有用,写入文件的时候使用的编码
autoCloseStreamtrue自动关闭写入的流。
password读取文件的密码
inMemoryfalse是否在内存处理,默认会生成临时文件以节约内存。内存模式效率会更好,但是容易OOM
writeExcelOnExceptionfalse写入过程中抛出异常了,是否尝试把数据写入到excel

WriteSheet
  • 设置方法如下(找不到参数的看下通用参数里面是否存在)

    EasyExcel.write(fileName, DemoData.class)
        .sheet("模板")
        // 在 sheet 方法之后, 在 doWrite方法之前都是设置WriteSheet的参数
        .doWrite(() -> {
            // 分页查询数据
            return data();
        });
    

    参数说明:

    名称默认值描述
    sheetNo0需要写入的编码
    sheetName需要些的Sheet名称,默认同sheetNo

WriteTable
  • 设置方法如下(找不到参数的看下通用参数里面是否存在)

    EasyExcel.write(fileName, DemoData.class)
        .sheet("模板")
        .table()
        // 在 table 方法之后, 在 doWrite方法之前都是设置WriteTable的参数
        .doWrite(() -> {
            // 分页查询数据
            return data();
        });
    

    参数说明:

    名称默认值描述
    tableNo0需要写入的编码

代码示例

创建 Excel 文件

(1)配置实体类

在 EasyExcel 中,通过 java 代码生成或者解析 Excel 文件很简单。不需要创建繁琐的表头

  • 创建数据行的实体类 : ContractProductVo

  • 在实体类上通过注解配置 :标题,行高,列宽等数据

    • @ExcelProperty:在创建Excel的时候,自动的读取实体类中的注解配置,生成表头
  • 示例代码如下

    import com.alibaba.excel.annotation.ExcelProperty;
    import com.alibaba.excel.annotation.format.DateTimeFormat;
    import com.alibaba.excel.annotation.write.style.ColumnWidth;
    import com.alibaba.excel.annotation.write.style.ContentRowHeight;
    import com.alibaba.excel.annotation.write.style.HeadRowHeight;
    import lombok.Data;
    import java.util.Date;
    
    /**
     * Excel 实体类
     */
    @Data
    @ContentRowHeight(20)  //数据行高数
    @HeadRowHeight(20)     //表头高度
    @ColumnWidth(15)       //列宽
    public class UserExcelVo {
    
        @ExcelProperty("用户名")
        private String username;
    
        @ExcelProperty("密码")
        private String password;
    
        @ExcelProperty("电话")
        private String phone;
    
        @ExcelProperty("创建时间")
        @DateTimeFormat("yyyy-MM-dd")
        private Date createTime;
    }
    

(2)Excel 文件的生成和下载

  • 在 java 代码中通过 EasyExcel 工具类完成 excel 文件的生成和下载

  • 示例代码如下

    	/**
    	 * 使用 EasyExcel完成 excel 的生成和下载
    	 *  1、数据查询
    	 *  2、设置下载信息
    	 *  3、调用EasyExcel的工具类完成生成下载
    	 */
    	@RequestMapping("/printEasyExcel")
    	public void printEasyExcel(String inputDate) throws IOException {
    		//1、数据查询
    		List<ContractProductVo> list = contractService.findByShipTime(inputDate+"%");
    		//2、设置下载信息
    		response.setContentType("application/vnd.ms-excel"); //下载excel
    		response.setCharacterEncoding("utf-8");
    		String fileName = URLEncoder.encode("出货表", "UTF-8");
    		response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
    		//3、调用EasyExcel的工具类完成生成下载
    		EasyExcel.write(response.getOutputStream())
    				.head(ContractProductVo.class) //设置表头
    				.sheet("test127") //指定页名称
    				.doWrite(list); //设置数据
    	}
    

模板打印

对于复杂样式的 Excel 文件创建,EasyExcel 支持模板打印。以一个 Excel 文件作为模板,自动的进行数据的拓展和填充。

  • 定义模板 ( 配置好了所有的样式 )
  • 配置实体类(表头)
  • java代码填充模板并下载 Excel

(1)定义模板

  • 在 easyExcel 中,支持两个模板语言
  • 语法:{map中的key}
    • 传入一个 map 集合:往往是用于处理非list集合的数据
  • 语法:{.对象中的属性名}
    • 传入一个 list 集合,会自动的循环 list 集合构造数据列表

在这里插入图片描述


(2)填充模板并下载 Excel 模板文件

	/**
	 * 模板打印
	 *
	 */
	@RequestMapping("/printTemplate")
	public void printTemplate(String inputDate) throws IOException {
		//1.准备数据
		List<ContractProductVo> list = contractService.findByShipTime(inputDate+"%");
		Map map = new HashMap<>();
		inputDate = inputDate.replaceAll("-0","-").replaceAll("-","年");
		map.put("time",inputDate);
		map.put("title1","客户名称");
		//2.设置下载信息
		response.setContentType("application/vnd.ms-excel"); //下载excel
		response.setCharacterEncoding("utf-8");
		String fileName = URLEncoder.encode("出货表", "UTF-8");
		response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
		//3.加载excel模板
		String path = session.getServletContext().getRealPath("/")+"/make/tOUTPRODUCT.xlsx";
		//4.创建EasyExcel的excelWtire对象( 用于数据填充)
		ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
				.head(ContractProductVo.class) //设置表头
				.withTemplate(path) //加载模板
				.build();
		//获取sheet对象
		WriteSheet sheet = EasyExcel.writerSheet().build();
		//5.调用方法完成填充map数据
		excelWriter.fill(map,sheet);
		//6.调用方法完成填充list数据
		excelWriter.fill(list,sheet);
		//7.属性资源,完成下载
		excelWriter.finish(); //下载excel文件,释放内存资源
	}


解析 Excel 文件

(1)配置实体类

创建实体类对象(用于解析封装对象),配置表头注解,EasyExcel 自动的根据注解配置获取文件中对应行的内容

  • @ExcelIgnoreUnannotated:忽略默认表头配置
  • @ExcelPropertym:配置表头
import cn.itcast.domain.BaseEntity;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;

import java.io.Serializable;
import java.util.List;

/**
 * 合同下货物的实体类
 *  通过EasyExcel完成文件解析,在实体类上通过注解配置表头
 *  如果实体类中的属性,未配置@ExcelProperty
 *      默认按照属性的编写顺序,从excel中获取数据
 *      默认情况下,为配置注解的属性会影响数据封装
 *  在类上配置一个注解:忽略未配置注解的属性
 */
@ExcelIgnoreUnannotated
@Data
public class ContractProduct extends BaseEntity implements Serializable {

	private String id;

	@ExcelProperty("货号")
	private String productNo;		//货号
	private String productImage;	//图片路径
	@ExcelProperty("货物描述")
	private String productDesc;		//货描
	@ExcelProperty("装率")
	private String loadingRate;		//报运:装率    1/3
	@ExcelProperty("箱数")
	private Integer boxNum;			//报运:箱数    100
	@ExcelProperty("包装单位")
	private String packingUnit;		//包装单位:PCS/SETS   支/箱
	@ExcelProperty("数量")
	private Integer cnumber;		//数量                            300
	private Integer outNumber;		//报运:出货数量            200
	private Integer finished;		//报运:是否完成		no
	@ExcelProperty("要求")
	private String productRequest;	//要求
	@ExcelProperty("单价")
	private Double price;			//单价
	private Double amount;			//总金额,冗余
	private Integer orderNo;		//排序号
	private String contractId;      //合同号
	@ExcelProperty("生产厂家")
	private String factoryName;		//厂家名称,冗余字段
	private String factoryId;

	private List<ExtCproduct> extCproducts ;	//货物和附件,一对多
}


(2)解析 Excel 文件

修改 ContractProductController 对象,完成 EasyExcel 的文件批量上传

	/**
	 * 通过 EasyExcel 完成文件上传解析,批量货物保存
	 *  参数:购销合同id
	 *  参数:上传的excel文件对象
	 */
	@RequestMapping("/import")
	public String importExcel(String contractId, MultipartFile file) throws Exception {
		List<ContractProduct> list = EasyExcel.read(file.getInputStream())
				.head(ContractProduct.class) //设置表头,将数据转化为目标对象
				.sheet(0) //读取第一页数据
				.doReadSync(); //解析excel,获取所有的数据

		for (ContractProduct contractProduct : list) {
			System.out.println(contractProduct);
			contractProduct.setContractId(contractId);
			contractProduct.setCompanyId(getLoginCompanyId());
			contractProduct.setCompanyName(getLoginCompanyName());
		}
		contractProductService.saveAll(list);
		return  "redirect:/cargo/contractProduct/list.do?contractId="+contractId;
	}

拓展

csv 读写的支持

在3.0.0-beta1版本开始支持,读的时候会自动判断。写的时候指定 excelType 就行。

EasyExcel.write(fileName, DemoData.class)
   // 指定导出类型为csv
   .excelType(ExcelTypeEnum.CSV)
   .sheet("模板")
   .doWrite(() -> {
       // 分页查询数据
       return data();
   });

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/871644.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GD32双路CAN踩坑记录

GD32双路CAN踩坑记录 目录 GD32双路CAN踩坑记录1 问题描述2 原因分析3 解决办法4 CAN配置参考代码 1 问题描述 GD32的CAN1无法进入接收中断&#xff0c;收不到数据。 注&#xff1a;MCU使用的是GD32E50x&#xff0c;其他型号不确定是否一样&#xff0c;本文只以GD32E50x举例说…

Vue项目-三级联动的路由跳转与传参

三级联动组件的路由的跳转与传参 三级联动&#xff0c;用户可以点击的&#xff1a;一级分类、二级分类和三级分类 以商城项目为例&#xff0c;Home模块跳转到Search模块&#xff0c;以及会把用户选中的产品&#xff08;产品名字、产品ID&#xff09;在路由跳转的时候&#xff…

Q*算法深度猜猜:从Q-learning优化到智能决策

Q*算法深度猜猜&#xff1a;从Q-learning优化到智能决策 引言 在强化学习&#xff08;Reinforcement Learning&#xff09;中&#xff0c;Q-learning算法作为一种无模型的学习方法&#xff0c;被广泛应用于解决各种决策优化问题。然而&#xff0c;尽管Q-learning在许多场景下…

基于ssm+vue+uniapp的医院挂号预约系统小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

Matlab处理H5文件

1.读取h5文件 filenamexxx.h5; h5disp(filename) 2.h5文件保存为mat文件 读取 HDF5 文件中的数据 % 指定 HDF5 文件的路径 filename xxx.h5;% 读取 HDF5 文件中的各个数据集 A241_P h5read(filename, /A241_P); A241_W h5read(filename, /A241_W); A242_P h5read(filen…

Ps:首选项 - 性能

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“性能” Performance选项卡允许用户通过调整内存使用、GPU 设置、高速缓存设置以及多线程处理等选项&#xff0c;来优化 Photoshop 的性能。这对于处理大文件、复杂图像或需要…

【NOI-题解】1137 - 纯粹素数1258 - 求一个三位数1140 - 亲密数对1149 - 回文数个数

文章目录 一、前言二、问题问题&#xff1a;1137 - 纯粹素数问题&#xff1a;1258 - 求一个三位数问题&#xff1a;1140 - 亲密数对问题&#xff1a;1149 - 回文数个数 三、感谢 一、前言 欢迎关注本专栏《C从零基础到信奥赛入门级&#xff08;CSP-J&#xff09;》 本章节主要…

二进制安装php

下载php二进制包&#xff1a; 官网地址&#xff1a;https://www.php.net/releases/ PHP: Releaseshttps://www.php.net/releases/在里边可以选择自己要下载的包进行下载&#xff1b; 下载完成后进行解压&#xff1a; tar xvzf php-7.3.12.tar.gz 解压后 进入目录进行预编…

学习yolo+Java+opencv简单案例(三)

主要内容&#xff1a;车牌检测识别&#xff08;什么颜色的车牌&#xff0c;车牌号&#xff09; 模型作用&#xff1a;车牌检测&#xff0c;车牌识别 文章的最后附上我的源码地址。 学习还可以参考我前两篇博客&#xff1a; 学习yoloJavaopencv简单案例&#xff08;一&#xff0…

Cobalt Strike 4.8 用户指南-第二节-用户界面

2.1、概述 Cobalt Strike用户界面分为两部分。界面顶部显示会话或目标的可视化。界面底部显示与你交互的每个 Cobalt Strike 功能或会话的选项卡。可以单击这两个部分之间的区域并根据自己的喜好调整它们的大小。 # 2.2、工具栏 顶部的工具栏提供对常见 Cobalt Strike功能的快…

C/C++实现蓝屏2.0

&#x1f680;欢迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e; &#x1f680;关注博主&#xff0c;后期持续更新系列文章 &#x1f680;如果有错误感谢请大家批评指出&#xff0c;及时修改 &#x1f680;感谢大家点赞&#x1f44d;收藏⭐评论✍ 前…

LeetCode合并两个有序链表

题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例 2&#xff1a; 输入&#xff1a;l1 [], l2…

chromedriver下载地址大全(包括124.*后)以及替换exe后仍显示版本不匹配的问题

Chrome for Testing availability CNPM Binaries Mirror 若已经更新了系统环境变量里的chromdriver路径下的exe&#xff0c;仍显示版本不匹配&#xff1a; 则在cmd界面输入 chromedriver 会跳出version verison与刚刚下载好的exe不匹配&#xff0c;则再输入&#xff1a; w…

http连接未释放导致生产故障

凌晨4点运维老大收到报警&#xff08;公司官网页面超时&#xff0c;上次故障因为运维修改nginx导致官网域名下某些接口不可用后&#xff0c;运维在2台nginx服务器上放了检测程序&#xff0c;检测官网页面&#xff09;&#xff0c;运维自己先看了看服务器相关配置&#xff0c;后…

小区物业维修管理系统/小区居民报修系统

摘要 小区物业维修是物业公司的核心&#xff0c;是必不可少的一个部分。在物业公司的整个服务行业中&#xff0c;业主担负着最重要的角色。为满足如今日益复杂的管理需求&#xff0c;各类小区物业维修管理系统也在不断改进。本课题所设计的小区物业维修管理系统&#xff0c;使用…

IPC进程间通信方式及网络通信

一、IPC进程间通信方式 1.共享内存&#xff08;最高效的进程间通信方式&#xff09; 其允许两个或多个进程共享一个给定的存储区&#xff0c;这一段存储区可以被两个或以上的进程映射至自己的地址空间中&#xff0c;一个进程写入共享内存的信息&#xff0c;可以被其他使用这个…

“面试宝典:高频算法题目详解与总结”

干货分享&#xff0c;感谢您的阅读&#xff01; &#xff08;暂存篇---后续会删除&#xff0c;完整版和持续更新见高频面试题基本总结回顾&#xff08;含笔试高频算法整理&#xff09;&#xff09; 备注&#xff1a;引用请标注出处&#xff0c;同时存在的问题请在相关博客留言…

每日掌握一个科研插图·2D密度图|24-08-21

小罗碎碎念 在统计学和数据可视化领域&#xff0c;探索两个定量变量之间的关系是一种常见的需求。为了更深入地理解这种关系&#xff0c;我们可以使用多种图形表示方法&#xff0c;这些方法在本质上是对传统图形的扩展和变体。 散点图&#xff1a;这是最基本的图形&#xff0c…

图算法-贪心策略-最小生成树(prim)和最短路径(dijkstra)

参考来源&#xff1a;和感谢 1.代码随想录 (programmercarl.com) 2.【图-最小生成树-Prim(普里姆)算法和Kruskal(克鲁斯卡尔)算法】https://www.bilibili.com/video/BV1wG411z79G?vd_source0ddb24a02523448baa69b0b871ab50f7 3.【图-最短路径-Dijkstra(迪杰斯特拉)算法】ht…

Vue3学习笔记之插槽

目录 前言 一、基础 (一) 默认插槽 (二) 具名插槽 (三) 作用域插槽 (四) 动态插槽 二、实战案例 前言 插槽&#xff08;Slots&#xff09;&#xff1f; 插槽可以实现父组件自定义内容传递给子组件展示&#xff0c;相当于一块画板&#xff0c;画板就是我们的子组件&…