EasyExcel是什么?

快速、简单避免OOM的java处理Excel工具

一、项目需求

从mongo库中查询数据,导出到excel文件中。但是动态导出的excel有多少列、列名是什么、有多少sheet页都需要动态获取。所以生成的excel也必须是动态生成,不能通过注解配置对象映射。而且写入的数据量,有可能达到100W级,使用传统的POI工具,需要把excel数据全部加载到内存空间,内存空间很容易OOM。所以选择了阿里的EasyExcel,据说可以高效的解决POI的OOM问题。

二、测试Demo

1、引入的pom依赖

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.2-beta5</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>

2、测试代码

package com.movitech.product.datahub.util;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.context.WriteContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.event.WriteHandler;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.Table;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.ExcelBuilderImpl;
import org.apache.poi.ss.usermodel.*; import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List; /**
* @Author JAY
* @Date 2019/8/29 11:00
* @Description TODO
**/
public class EasyExcelUtil { public static String excelFilePath = "C:\\Users\\lenovo\\Desktop\\Jay01-(jay01)-v5自定义导入数据.xls"; public static void main(String[] args) {
try {
writeExcel(excelFilePath);
} catch (IOException e) {
e.printStackTrace();
}
} public static void writeExcel(String excelFile) throws IOException {
// 文件输出位置
OutputStream out = new FileOutputStream(excelFile);
ExcelWriter writer = EasyExcelFactory.getWriter(out); // 动态添加表头,适用一些表头动态变化的场景
Sheet sheet1 = new Sheet(1, 0);
sheet1.setSheetName("第一个sheet");
// 创建一个表格,用于 Sheet 中使用
Table table1 = new Table(1);
// 无注解的模式,动态添加表头
table1.setHead(createTestListStringHead());
// 写数据
writer.write1(new ArrayList<>(), sheet1, table1); // 动态添加表头,适用一些表头动态变化的场景
Sheet sheet2 = new Sheet(2, 0);
sheet2.setSheetName("第2个sheet");
/*
添加TableStyle属性会使内存OOM,没办法满足分批插入100W条数据
TableStyle tableStyle = new TableStyle();
com.alibaba.excel.metadata.Font font = new com.alibaba.excel.metadata.Font();
font.setBold(true);
tableStyle.setTableContentFont(font);
sheet2.setTableStyle(tableStyle);
*/ // 创建一个表格,用于 Sheet 中使用
Table table2 = new Table(2);
// 无注解的模式,动态添加表头
table2.setHead(createTestListStringHead());
writer.write1(new ArrayList<>(), sheet2, table2); int x = 0;
while (x < 1000000) {
     // 模拟分批写入数据到excel,每次写入100条
System.out.println("x = " + x);
Table tableX = new Table(1);         // 每次从sheet的第几行开始写入
sheet1.setStartRow(x);
writer.write1(createDynamicModelList(x), sheet1, tableX); Table tableX2 = new Table(1);
sheet2.setStartRow(x);
writer.write1(createDynamicModelList(x), sheet2, tableX2); x = x + 100;
}
// 将上下文中的最终 outputStream 写入到指定文件中
writer.finish();
// 关闭流
out.close();
} private static List<List<Object>> createDynamicModelList(int x) {
List<List<Object>> rows = new ArrayList<>();
for (int i= x; i < 100 + x; i++){
List<Object> row = new ArrayList<>();
row.add("字符串-" + i);
row.add(Long.valueOf(187837834L) + i);
row.add(Integer.valueOf(2233 + i));
row.add("宁-" + i);
row.add("微信公众号: demo");
rows.add(row);
} return rows;
} private static List<List<String>> createTestListStringHead() {
// 模型上没有注解,表头数据动态传入
List<List<String>> head = new ArrayList<List<String>>();
List<String> headCoulumn1 = new ArrayList<String>();
List<String> headCoulumn2 = new ArrayList<String>();
List<String> headCoulumn3 = new ArrayList<String>();
List<String> headCoulumn4 = new ArrayList<String>();
List<String> headCoulumn5 = new ArrayList<String>();
headCoulumn1.add("第1列");
headCoulumn2.add("第2列");
headCoulumn3.add("第3列");
headCoulumn4.add("第4列");
headCoulumn5.add("第5列"); head.add(headCoulumn1);
head.add(headCoulumn2);
head.add(headCoulumn3);
head.add(headCoulumn4);
head.add(headCoulumn5);
return head;
} }

3、执行结果

总结:

此测试代码可以直接运行测试查看结果。

我配置的jvm运行参数,

我只给了10M空间,但是往excel中写入100W数据,程序并没有出现OOM。可以看到,使用EasyExcel,确实解决了OOM问题。

但是实际情况,EasyExcel不足以满足我的业务需求。因为除了百万级的数据导出之外,还需要进行sheet页隐藏、行隐藏、列隐藏等操作。目前EasyExcel的API,还没有那么多的功能变化。不过,easyExcel提供了自定义拦截器的功能,貌似可以给excel做样式处理。大致测试了一下,可以隐藏列和sheet,但是不知道怎么隐藏行。测试代码如下:

(1)隐藏列,通过自定义拦截器

public static void writeExcelToSheet(String excelFile, Sheet sheet) throws IOException {
// 文件输出位置
OutputStream out = new FileOutputStream(excelFile);
ExcelWriter writer = EasyExcelFactory.getWriterWithTempAndHandler(null, out, ExcelTypeEnum.XLS, true, new WriteHandler() {
@Override
public void sheet(int i, org.apache.poi.ss.usermodel.Sheet sheet) {
sheet.setColumnHidden(0,true);
sheet.setColumnHidden(1,true);
} @Override
public void row(int i, Row row) {
System.out.println("row : " + row.getRowNum());
} @Override
public void cell(int i, Cell cell) {
System.out.println("cell : " + i);
}
}); Table table1 = new Table(1);
table1.setHead(createTestListStringHead());// 写数据
writer.write1(createDynamicModelList(0), sheet, table1); // 将上下文中的最终 outputStream 写入到指定文件中
writer.finish();
// 关闭流
out.close();
}

(2)隐藏sheet页,通过反射获取Workbook,用wb来设置隐藏sheet页

/**
* **获取workbook**
* 因为EasyExcel这个库设计的原因
* 只能使用反射获取workbook
*
* @param writer
* @return
*/
private static Workbook getWorkbook(ExcelWriter writer) {
Workbook workbook = null;
try {
Class<?> clazz1 = Class.forName("com.alibaba.excel.ExcelWriter");
Field[] fs = clazz1.getDeclaredFields();
for (Field field : fs) {
// 要设置属性可达,不然会抛出IllegalAccessException异常
field.setAccessible(true);
if ("excelBuilder".equals(field.getName())) {
ExcelBuilderImpl excelBuilder = (ExcelBuilderImpl) field.get(writer);
Class<?> clazz2 = Class.forName("com.alibaba.excel.write.ExcelBuilderImpl");
Field[] fs2 = clazz2.getDeclaredFields();
for (Field field2 : fs2) {
field2.setAccessible(true);
if ("context".equals(field2.getName())) {
WriteContext context = (WriteContext) field2.get(excelBuilder);
workbook = context.getWorkbook();
}
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return workbook;
}
public static void writeExcel(String excelFile) throws IOException {
// 文件输出位置
OutputStream out = new FileOutputStream(excelFile);
ExcelWriter writer = EasyExcelFactory.getWriter(out); // 动态添加表头,适用一些表头动态变化的场景
Sheet sheet1 = new Sheet(1, 0);
sheet1.setSheetName("第一个sheet");
// 创建一个表格,用于 Sheet 中使用
Table table1 = new Table(1);
// 无注解的模式,动态添加表头
table1.setHead(createTestListStringHead());
// 写数据
writer.write1(new ArrayList<>(), sheet1, table1); // 动态添加表头,适用一些表头动态变化的场景
Sheet sheet2 = new Sheet(2, 0);
sheet2.setSheetName("第2个sheet");
/*
添加TableStyle属性会使内存OOM
TableStyle tableStyle = new TableStyle();
com.alibaba.excel.metadata.Font font = new com.alibaba.excel.metadata.Font();
font.setBold(true);
tableStyle.setTableContentFont(font);
sheet2.setTableStyle(tableStyle);
*/ // 创建一个表格,用于 Sheet 中使用
Table table2 = new Table(2);
// 无注解的模式,动态添加表头
table2.setHead(createTestListStringHead());
writer.write1(new ArrayList<>(), sheet2, table2); int x = 0;
while (x < 10000) {
System.out.println("x = " + x);
Table tableX = new Table(1);
sheet1.setStartRow(x);
writer.write1(createDynamicModelList(x), sheet1, tableX); Table tableX2 = new Table(1);
sheet2.setStartRow(x);
writer.write1(createDynamicModelList(x), sheet2, tableX2); x = x + 100;
} //获取workbook,隐藏第2页sheet
Workbook workbook = getWorkbook(writer);
workbook.setSheetHidden(1,true);
// 将上下文中的最终 outputStream 写入到指定文件中
writer.finish();
// 关闭流
out.close();
}

参考资源 https://segmentfault.com/a/1190000019472781,https://github.com/alibaba/easyexcel

EasyExcel写入百万级数据到多sheet---非注解方式的更多相关文章

  1. poi实现百万级数据导出

    注意使用 SXSSFWorkbook 此类在构造表格和处理行高的时候效率极高,刚开始时我使用的 XSSFWorkbook 就出现构造表格效率极低,一万行基本需要3秒左右,那当导出百万级数据就慢的要死啦 ...

  2. 实战手记:让百万级数据瞬间导入SQL Server

    想必每个DBA都喜欢挑战数据导入时间,用时越短工作效率越高,也充分的能够证明自己的实力.实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本文将向大家推荐一个挑战4秒极限让百万级数据瞬间 ...

  3. 【转 】实战手记:让百万级数据瞬间导入SQL Server

    想必每个DBA都喜欢挑战数据导入时间,用时越短工作效率越高,也充分的能够证明自己的实力.实际工作中有时候需要把大量数据导入数据库,然后用于各种程序计算,本文将向大家推荐一个挑战4秒极限让百万级数据瞬间 ...

  4. 详解如何挑战4秒内百万级数据导入SQL Server(转)

      对于大数据量的导入,是DBA们经常会碰到的问题,在这里我们讨论的是SQL Server环境下百万级数据量的导入,希望对大家有所帮助.51CTO编辑向您推荐<SQL Server入门到精通&g ...

  5. 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(37)-文章发布系统④-百万级数据和千万级数据简单测试

    原文:构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(37)-文章发布系统④-百万级数据和千万级数据简单测试 系列目录 我想测试EF在一百万条数据下的显示时间! ...

  6. EF查询百万级数据的性能测试--多表连接复杂查询

    相关文章:EF查询百万级数据的性能测试--单表查询 一.起因  上次做的是EF百万级数据的单表查询,总结了一下,在200w以下的数据量的情况(Sql Server 2012),EF是可以使用,但是由于 ...

  7. Sql Server中百万级数据的查询优化

    原文:Sql Server中百万级数据的查询优化 万级别的数据真的算不上什么大数据,但是这个档的数据确实考核了普通的查询语句的性能,不同的书写方法有着千差万别的性能,都在这个级别中显现出来了,它不仅考 ...

  8. Mongo查询百万级数据性能问题及JAVA优化问题

    Mongo查询百万级数据  使用分页  skip和limit 效率会相当慢   那么怎么解决呢  上代码 全部查询数据也会特别慢 Criteria criteria = new Criteria(); ...

  9. Excel导入数据库百万级数据瞬间插入

    Excel导入数据库百万级数据瞬间插入 百万级别,瞬间,有点吊哇

随机推荐

  1. Oracle队列实现

    Oracle队列实现 -- 核心技术点:for update 创建测试表 create table t ( id       number primary key, processed_flag va ...

  2. 想知道使用OPC服务器时如何设置DCOM?看完本文就懂了(下)

    接上文...... “安全”选项卡“安全”选项卡上,有3个选项需要设置. 启动权限 选择“使用默认值”选项 访问权限 选择“使用默认值”选项 配置权限 选择“自定义”选项,然后单击“编辑” 将打开一个 ...

  3. 机器学习 | 聚类分析总结 & 实战解析

    聚类分析是没有给定划分类别的情况下,根据样本相似度进行样本分组的一种方法,是一种非监督的学习算法.聚类的输入是一组未被标记的样本,聚类根据数据自身的距离或相似度划分为若干组,划分的原则是组内距离最小化 ...

  4. jmeter bodydata参数传递

    参见:接口测试, jmeter bodydata 传送的参数,在请求中显示为空 新增http header信息头:

  5. glfw之hello world

    mac上用cocoa做imshow,资料似乎不好找,即便找到也需要和OC混编,而不是纯C.这不够纯粹.考虑用opengl做通用的.跨平台的imshow.先入门一下opengl,从glfw官方例子入手. ...

  6. linux查看磁盘分区

    df  查看磁盘分区使用状况 用法:df [选项]... [文件]... Show information about the file system on which each FILE resid ...

  7. PAT甲级1002水题飘过

    #include<iostream> #include<string.h> using namespace std; ]; int main(){ int n1, n2; wh ...

  8. .NET 使用 VLC 播放视频

    使用 VLC 播放监控有几个月了,现在是多个项目中都有用到.在使用的过程中也有一些细节供大家参考. 一.对 VLC 的了解 VLC 是一个开源的跨平台多媒体播放器及框架. VLC 官方出的有播放器.编 ...

  9. 项目Beta冲刺 - 凡事预则立

    课程: 软件工程1916|W(福州大学) 作业要求: 项目Beta冲刺 团队名称: 火鸡堂 作业目标: 尽力交付 火鸡堂 队员学号 队员姓名 博客地址 备注 221600111 彼术向 http:// ...

  10. php数组打乱顺序

    shuffle() PHP shuffle() 函数随机排列数组单元的顺序(将数组打乱).本函数为数组中的单元赋予新的键名,这将删除原有的键名而不仅是重新排序. 语法: bool shuffle ( ...