【小记】高性能能Excel操作工具-EasyExcel

一、初识EasyExcel

首先,说到EasyExcel,有做过报表的导入以及导出的小伙伴一定不陌生。
比如,目前就职的公司所涉及的CRM类的诸多项目,就用到了此类功能。
利用Java来对Excel进行导入以及导出,现在常见的框架主要有两大类。

  1. Apache POI
  2. Alibaba EasyExcecl

1 POI

1.1 占用内存,消耗资源

比如我就是先了解的Excel导出工具,就是POI,但是被POI的一些功能缺点所苦恼,众所周知,POI是一款基于内存的读写模式,在一些的大型项目,高并发场景下就显得吃力。

1.2 学习周期较长,比使用用于快速上手

了解过POI的小伙伴都知道,POI操作Excel分为了两种模式(SAAX、Dom)
且SAX解析Excel较为复杂,且POI针对不同版本的Excel,读取和存储的方式也是不相同。
且代码两十分复杂,虽有一些规律可循,但是长期不巩固,很容易就会生疏

1.3 POI的特点

  1. 功能强大
  2. 代码复杂
  3. 内存消耗大

2 EasyExcel

【小记】高性能能Excel操作工具-EasyExcel_第1张图片
这张图,是EasyExcel官方的文档上的一张图,这张图很好的诠释了他高性能的地方,内存消耗低。

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

【小记】高性能能Excel操作工具-EasyExcel_第2张图片
官网 :[EasyExcel官网](https://easyexcel.opensource.alibaba.com/)

2.1 EasyExcel特点

  1. 对POI进行封装 使用简单
  2. 内存消耗低
  3. 只能操作Excel

二、环境搭建

2.1 加入依赖


		<dependency>
			<groupId>com.alibabagroupId>
			<artifactId>easyexcelartifactId>
			<version>3.1.1version>
		dependency>

2.2 创建数据库

create table `t_info` (
	`nick_name` varchar (765),
	`net_number` varchar (765),
	`sign_time` date ,
	`belong_org` varchar (765),
	`order_num` bigint (20),
	`income` bigint (20),
	`in_month` int (11)
); 

2.3 创建实体类

@Data
public class Information {

	private String netNumber;
	
	private String nickName;

	private Date signTime;

	private String belongOrg;
    
	private Long orderNum;
    
	private Long income;
	
	private int month;

三、简单读写

3.1 简单的读取操作

首先,我们需要认识一下Excel,在Excel里,每一个Excel都可以称作一个WorkBook,在WorkBook里面又可以分为多个sheet。
读取Excel里的文件,其实只需要用到一个方法EasyExcel.read()即可
【read方法的源码】

/**
     * @param pathName
     *            读取文件的路径地址
     * @param head
     *            解析成Java实体类的类
     * @param readListener
     *            读的监听器
     * @return Excel reader builder.
     */
public static ExcelReaderBuilder read(String pathName, Class head, ReadListener readListener) {
        ExcelReaderBuilder excelReaderBuilder = new ExcelReaderBuilder();
        excelReaderBuilder.file(pathName);
        if (head != null) {
            excelReaderBuilder.head(head);
        }
        if (readListener != null) {
            excelReaderBuilder.registerReadListener(readListener);
        }
        return excelReaderBuilder;
    }

【读的监听器】

public class EasyExcelListener extends AnalysisEventListener<Information> {
	
	private static final Logger log = LoggerFactory.getLogger(EasyExcelListener.class);

	/**
	* invoke方法尾调用read读取文件的时候所执行
	* T 为指定读取实体类型
	*/
	@Override
	public void invoke(Information info, AnalysisContext context) {
		// TODO Auto-generated method stub
		log.info("开始读取数据============>>>");
		log.info("information = " + info);
	}

	@Override
	public void doAfterAllAnalysed(AnalysisContext context) {}

}

【测试案例】

@Test
public void testRead() {
	ExcelReaderBuilder workBook = EasyExcel.read("测试EasyExcel简单的读取.xlsx",Information.class,new EasyExcelListener());
	ExcelReaderSheetBuilder sheet = workBook.sheet();
	sheet.doRead();
}

【小记】高性能能Excel操作工具-EasyExcel_第3张图片

3.2 简单的写入操作

EasyExcel也提供了同样简单的写出操作的方法EasyExcel.Write()
【Write源码】

 public static ExcelWriterBuilder write(String pathName, Class head) {
        ExcelWriterBuilder excelWriterBuilder = new ExcelWriterBuilder();
        excelWriterBuilder.file(pathName);
        if (head != null) {
            excelWriterBuilder.head(head);
        }
        return excelWriterBuilder;
    }

【测试案例】

@Test
	public void testEasyWrite() {
		String fileName = "测试EasyExcel简单的写入01.xlsx";
		QueryWrapper<Information> wrapper = new QueryWrapper<>();
//		wrapper.like("belong_org", "小");
		Page<Information> page = new Page<>(1, 10);
		IPage<Information> iPage = inService.page(page, wrapper);
		List<Information> list = iPage.getRecords();
		EasyExcel.write(fileName, Information.class).sheet("某直播平台3、4月份数据信息").doWrite(list);
	}

【小记】高性能能Excel操作工具-EasyExcel_第4张图片
这里可以看到,这里的列宽,和表头都有点不太合适。为了可以更加可视化,方便阅读的展示数据,easyExcel提供了更多的注解

四、常用注解

4.1 @ExcelProperty

用于将实体类的名称与Excel导出的列名称对应,用于在属性字段上
【ExcelProperty源码】

public @interface ExcelProperty {

    /**
     * sheet表头的名称
     * 

* 写操作:可以自动地将表头合并,也可以单独显示 *

* 读操作: 当有多个表头,可以拿到最后面的 */ String[] value() default {""}; /** * 列的索引 * * 读或写的时候,可以改便列的顺序,如果index = -1 则会按照order排序 * * 优先级 index >= sort >= default sort */ int index() default -1; /** * Defines the sort order for an column. * * priority: index > order > default sort * * @return Order of column */ int order() default Integer.MAX_VALUE; /** * Force the current field to use this converter. * * @return Converter */ Class<? extends Converter<?>> converter() default AutoConverter.class; /** * * default @see com.alibaba.excel.util.TypeUtil if default is not meet you can set format * * @return Format string * @deprecated please use {@link com.alibaba.excel.annotation.format.DateTimeFormat} */ @Deprecated String format() default ""; }

4.1.1 参数说明

属性 功能 备注
value sheet的标头名称 是一个字符数组,如果写多个,相同的为主标头,其余为附表头
index sheet列的索引(从0开始) 控制列的顺序,通常不与value同用。如果不连续,则会出现空列
convert 转化器 通常用于性别的值映射(1–男;0–女)

【测试案例】

@TableName("t_info")
public class Information {

	@ExcelProperty({"网站号","网站号"})
	private String netNumber;
	
	@ExcelProperty({"大神昵称","大神昵称"})
	private String nickName;
	
	@ExcelProperty({"进站时间","进站时间"})
	private Date signTime;

	@ExcelProperty({"所属大神团","所属大神团"})
	private String belongOrg;

	@ExcelProperty({"营收信息","订单数量"})
	private Long orderNum;
	
	@ExcelProperty({"营收信息","收入"})
	private Long income;
	
	@ExcelProperty({"月份","月份"})
	@TableField("in_month")
	private int month;
}

说明:如果是在里面写一个数组的形式,则会把相同的表头合并表头

【小记】高性能能Excel操作工具-EasyExcel_第5张图片

4.2 @ColumnWidth

设置列宽度,只有一个参数value,取值范围为0-255。因为Excel一个单元格的取值范围就为0-255个字符。
【测试案例】

public class Information {

	@ExcelProperty({"网站号","网站号"})
	@ColumnWidth(20)
	private String netNumber;
	
	@ExcelProperty({"大神昵称","大神昵称"})
	@ColumnWidth(20)
	private String nickName;
	
	@ExcelProperty({"进站时间","进站时间"})
	@ColumnWidth(20)
	private Date signTime;

	@ColumnWidth(20)
	@ExcelProperty({"所属大神团","所属大神团"})
	private String belongOrg;

	@ExcelProperty({"营收信息","订单数量"})
	@ColumnWidth(20)
	private Long orderNum;
	
	@ExcelProperty({"营收信息","收入"})
	@ColumnWidth(20)
	private Long income;
	
	@ExcelProperty({"月份","月份"})
	@TableField("in_month")
	private int month;
}

【小记】高性能能Excel操作工具-EasyExcel_第6张图片

4.3 @ContentFontStyle | @HeadFontStyle

ContentFontStyle:用于设置单元格内容字体格式的注解。
HeadFontStyle:用于设置表头单元格字体格式的注解

4.3.1参数说明

属性 功能 备注
fontName 字体名称
fontHeightInPoints 字体高度
italic 是否斜体
color 颜色
typeOffset 偏移量
blod 是否加粗
charset 编码格式
underline 下划线

@ContentFontStyle 用于设置单元格内容字体格式的注解
【测试案例】

@TableName("t_info")
@ContentFontStyle(color = 14,fontHeightInPoints = 18,fontName = "宋体")
@HeadFontStyle(fontName = "华文中宋",color = 10,fontHeightInPoints = 24)
public class Information {

	@ExcelProperty({"网站号","网站号"})
	@ColumnWidth(20)
	private String netNumber;
	
	@ExcelProperty({"大神昵称","大神昵称"})
	@ColumnWidth(20)
	private String nickName;
	
	@ExcelProperty({"进站时间","进站时间"})
	@ColumnWidth(20)
	private Date signTime;

	@ColumnWidth(20)
	@ExcelProperty({"所属大神团","所属大神团"})
	private String belongOrg;

	@ExcelProperty({"营收信息","订单数量"})
	@ColumnWidth(20)
	private Long orderNum;
	
	@ExcelProperty({"营收信息","收入"})
	@ColumnWidth(20)
	private Long income;
	
	@ExcelProperty({"月份","月份"})
	@TableField("in_month")
	private int month;
}

【小记】高性能能Excel操作工具-EasyExcel_第7张图片

4.4 ContentLoopMerge

用于设置单元格合并

4.4.1 参数说明

属性 功能 备注
eachRow
columnExtend

4.5 contentRowHeight|HeadRowHeight

全局设置行高,以及设置表头的行高。 通常用于类上

4.5.1 参数说明

属性 功能 备注
value 行高 -1表示自动行高

4.6 ContentStyle | HeadStyle

ContentStyle:设置内容的样式,通常用于字段值上,表示设置某一列单元格的样式。
HeadStyle:设置表的表头的样式,通常用于类上。

4.6.1 参数说明

属性 功能 备注
dateFormat 日期格式
hidden 设置单元格使用此样式隐藏
locked 设置单元格使用此样式锁定
quotePrefix 在单元格前面增加`符号,数字或者公式将以文符串的形式展示
horizontalAlignment 是否水平居中
wrapped 是否换行 为true是,表示多行上显示内容
fillFocegroundColor 设置单元格填充的前景色 值为short类型的值

这里需要注意一下,Excel里的所有颜色,对应到代码里,都是由ICellStyle这个接口里的FillFoceGroundColor属性所设置。如果需要颜色的取值,可以参考:https://www.cnblogs.com/byxxw/p/5265127.html,这边博客。

4.7 DateTimeFormat

用于设置时间类型的格式化操作

4.7.1 参数说明

常用的参数就一个value,指定格式化时间的格式

@ExcelProperty({"进站时间","进站时间"})
@DateTimeFormat("yyyy-MM-dd")
@ColumnWidth(20)
private Date signTime;

【小记】高性能能Excel操作工具-EasyExcel_第8张图片

4.8 NumberFormat

用于设置数值类型的数据单元格格式化

4.8.1 参数说明

同上,也是一个value参数

@ExcelProperty({"营收信息","收入"})
@ColumnWidth(20)
@NumberFormat("#.0")
private Long income;

【小记】高性能能Excel操作工具-EasyExcel_第9张图片

说明:数字、时间格式和Excel里面设置单元格格式一样,#表示任意数字,没有不会填充;0表示一个数字,没有补0。

五、实现文件上传下载

5.1 文件上传

首先由于我们使用的是SpringBoot的方式完成,所以,我们需要导入相关的文件上传下载依赖文件fileupload


		<dependency>
			<groupId>commons-fileuploadgroupId>
			<artifactId>commons-fileuploadartifactId>
			<version>1.3.3version>
		dependency>

其次,需要使用springmvc的相关控制层,完成文件上传的测试,springmvc配置:


<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
	http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.springframework.org/schema/mvc
	http://www.springframework.org/schema/mvc/spring-mvc.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context.xsd">

	
	<context:component-scan
		base-package="com.wei.controller" />
	
	<bean id="multipartResolver"
		class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="defaultEncoding" value="UTF-8" />
	bean>

beans>

web.xml

DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
	
	<context-param>
		
		<param-name>contextConfigLocationparam-name>
		
		<param-value>classpath:spring-mvc.xmlparam-value>
	context-param>

	
	<filter>
		<filter-name>characterEncodingFilterfilter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
		
		<init-param>
			<param-name>encodingparam-name>
			<param-value>UTF-8param-value>
		init-param>
		
		<init-param>
			<param-name>forceRequestEncodingparam-name>
			<param-value>trueparam-value>
		init-param>
		
		<init-param>
			<param-name>forceResponseEncodingparam-name>
			<param-value>trueparam-value>
		init-param>
	filter>
	<filter-mapping>
		<filter-name>characterEncodingFilterfilter-name>
		
		<url-pattern>/*url-pattern>
	filter-mapping>
	
	
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
	listener>

	
	<servlet>
		<servlet-name>springmvcservlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
		
		<init-param>
			
			<param-name>contextConfigLocationparam-name>
			
			<param-value>classpath:spring-mvc.xmlparam-value>
		init-param>
		
		<load-on-startup>1load-on-startup>
	servlet>
	
	<servlet-mapping>
		<servlet-name>springmvcservlet-name>
		
		<url-pattern>/url-pattern>
	servlet-mapping>
web-app>

Service层创建ReadExcel()方法完成文件上传时候数据读取操作

private static final Logger log = LoggerFactory.getLogger(ImformationServiceImpl.class);

@Override
public void readExcelFile(List<Information> infos) {
	// TODO Auto-generated method stub
	log.info("Information = " + infos);
}

controller层完成读取调用

@RestController
@RequestMapping("/file")
public class InfomationController {
	
	@Autowired
	private InformationListener listener;
	
	 @RequestMapping("/fileUpload")
	 public String testFileUpload(MultipartFile file, HttpSession session) {
	       
	     try {
	    	// 获取到工作普
			ExcelReaderBuilder workbook = EasyExcel.read(file.getInputStream(),
														 Information.class,
														 listener);
			// 工作表
			ExcelReaderSheetBuilder sheet = workbook.sheet();
			// 执行监听器里的invoke方法读取数据
			sheet.doRead();
			return "success";
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	     return "fail";
	}
}

配置监听器,完成数据的读取

@Component
@Scope("prototype")
public class InformationListener extends AnalysisEventListener<Information>{

	@Autowired
	private InformationService inService;
	
	private ArrayList<Information> list = new ArrayList<>();
	
	@Override
	public void invoke(Information data, AnalysisContext context) {
		list.add(data);
		if (list.size() % 5 == 0) {
			inService.readExcelFile(list);
			list.clear();
		}
	}

	@Override
	public void doAfterAllAnalysed(AnalysisContext context) {}

}

【小记】高性能能Excel操作工具-EasyExcel_第10张图片

5.1 文件下载

@RequestMapping("/fileDownload")
	 public void download(HttpServletResponse response) throws Exception{
		 response.setContentType("application/vnd.ms-excel");
		 response.setCharacterEncoding("UTF-8");
		 String filename = URLEncoder.encode("测试文件下载","UTF-8");
		 response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''"+filename+".xlsx");
		 ServletOutputStream outputStream = response.getOutputStream();
		 ExcelWriterBuilder workbook = EasyExcel.write(outputStream, Information.class);
		 ExcelWriterSheetBuilder sheet = workbook.sheet("测试写入数据");
		 List<Information> infos = createData();
		 sheet.doWrite(infos);
	 }

	private List<Information> createData() {
		ArrayList<Information> list = new ArrayList<>();
		Information info = null;
		for (int i = 0; i <= 10; i++) {
			info = new Information();
			info.setBelongOrg("测试大神团"+i);
			Random random = new Random();
			int s = random.nextInt(1000000)%(1000000-10000+1) + 10000;
			info.setIncome(Integer.toUnsignedLong(s));
			info.setMonth(3);
			info.setNetNumber("测试哈哈哈哈");
			info.setNetNumber("46565465");
			info.setOrderNum(45763L);
			info.setSignTime(new Date());
			list.add(info);
		}
		return list;
	}

你可能感兴趣的:(excel)