一、Apache 开源框架 poi、jxl 的缺陷

两者都存在生成 excel 文件不够简单优雅快速的问题。而且它们都还存在一个严重的问题,那就是非常耗内存,严重时会导致内存溢出。POI 虽然目前来说,是 excel 解析框架中被使用最广泛的,但这个框架并不完美。为什么这么说呢?

开发者们大部分使用 POI,都是使用其 userModel 模式。而 userModel 的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,但是还是可控的。

然而 userModel 模式最大的问题是在于,对内存消耗非常大,一个几兆的文件解析甚至要用掉上百兆的内存。现实情况是,很多应用现在都在采用这种模式,之所以还正常在跑是因为并发不大,并发上来后,一定会OOM或者频繁的FULL GC。

二、阿里的 EasyExcel

官方对其的简介是:快速、简单避免OOM的java处理Excel工具。

三、EasyExcel 解决了什么

  • 传统 Excel 框架,如 Apache poi、jxl 都存在内存溢出的问题;
  • 传统 excel 开源框架使用复杂、繁琐;
  • EasyExcel 底层还是使用的 poi,但是做了很多优化,比如修复了并发情况下的一些 bug,具体修复细节,可阅读官方文档 https://github.com/alibaba/easyexcel

四、pom 依赖

<!--alibaba easyexcel-->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>1.1.2-beta5</version>
</dependency>

五、代码示例

@Test
public void writeExcel() throws Exception {// 文件输出位置OutputStream out = new FileOutputStream("d:/test.xlsx");ExcelWriter writer = EasyExcelFactory.getWriter(out);// 写仅有一个 Sheet 的 Excel 文件, 此场景较为通用Sheet sheet = new Sheet(1, 0, WriteModel.class);// 第一个 sheet 名称sheet.setSheetName("first sheet");// 写数据到 Writer 上下文中// 入参1: 创建要写入的模型数据// 入参2: 要写入的目标 sheetwriter.write(createModelList(), sheet);// 将上下文中的最终 outputStream 写入到指定文件中writer.finish();// 关闭流out.close();
}

上面这段示例代码中,有两点很重要:
①WriteModel 这个对象就是要写入 Excel 的数据模型对象(表头 head,以及每个单元格内的数据顺序下文说明)
②创建需要写入的数据集,(正常业务中,这块都是从数据库中查询出来的)

1️⃣createModelList

private List<WriteModel> createModelList() {List<WriteModel> writeModels = new ArrayList<>();for (int i = 0; i < 100; i++) {WriteModel writeModel = WriteModel.builder().name("test" + i).password("123456").age(i + 1).build();writeModels.add(writeModel);}return writeModels;
}

2️⃣WriteModel

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class WriteModel extends BaseRowModel {@ExcelProperty(value = "姓名", index = 0)private String name;@ExcelProperty(value = "密码", index = 1)private String password;@ExcelProperty(value = "年龄", index = 2)private Integer age;
}

ExayExcel 提供注解的方式,来方便的定义 Excel 需要的数据模型:
①首先,定义的写入模型必须要继承自 BaseRowModel.java;
②通过 @ExcelProperty 注解来指定每个字段的列名称,以及下标位置

六、️动态生成 Excel 内容

上面的例子是基于注解的,也就是说表头 head以及内容都是写死的,换句话说,定义好了一个数据模型,那么,生成的 Excel 文件也就是只能遵循这种模型来了。但是,实际业务中可能会存在动态变化的需求,要怎么做?

   @Testpublic void writeExcel() throws Exception {// 文件输出位置OutputStream out = new FileOutputStream("d:/dynamic.xlsx");ExcelWriter writer = EasyExcelFactory.getWriter(out);// 动态添加表头,适用一些表头动态变化的场景Sheet sheet = new Sheet(1, 0);sheet.setSheetName("first sheet");// 创建一个表格,用于 Sheet 中使用Table table = new Table(1);//自定义表格样式table.setTableStyle(createTableStyle());// 无注解的模式,动态添加表头table.setHead(createTestListStringHead());// 写数据writer.write1(createDynamicModelList(), sheet, table);//可以通过 merge()方法来合并单元格//注意下标是从 0 开始的,也就是说合并了第六行到第七行,其中的第一列到第五列writer.merge(5, 6, 0, 4);// 将上下文中的最终 outputStream 写入到指定文件中writer.finish();// 关闭流out.close();}

1️⃣无注解模式,动态添加表头,也可自由组合复杂表头,代码如下:

 public 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("第一列");headCoulumn1.add("第一列");headCoulumn1.add("第一列");headCoulumn2.add("第一列");headCoulumn2.add("第一列");headCoulumn2.add("第一列");headCoulumn3.add("第二列");headCoulumn3.add("第二列");headCoulumn3.add("第二列");headCoulumn4.add("第三列");headCoulumn4.add("第三列2");headCoulumn4.add("第三列2");headCoulumn5.add("第四列");headCoulumn5.add("第4列");headCoulumn5.add("第5列");head.add(headCoulumn1);head.add(headCoulumn2);head.add(headCoulumn3);head.add(headCoulumn4);head.add(headCoulumn5);return head;}

2️⃣创建动态数据,注意这里的数据类型是 Object:

 private List<List<Object>> createDynamicModelList() {//所有行数据List<List<Object>> rows = new ArrayList<>();for (int i = 0; i < 100; i++) {//一行数据List<Object> row = new ArrayList<>();row.add("第" + i+"行");row.add(Long.valueOf(187837834L + i));row.add(Integer.valueOf(2233 + i));row.add("NMB");row.add("CBF");rows.add(row);}return rows;}

3️⃣自定义表头以及内容样式

public static TableStyle createTableStyle() {TableStyle tableStyle = new TableStyle();// 设置表头样式Font headFont = new Font();// 字体是否加粗headFont.setBold(true);// 字体大小headFont.setFontHeightInPoints((short) 12);// 字体headFont.setFontName("楷体");tableStyle.setTableHeadFont(headFont);// 背景色tableStyle.setTableHeadBackGroundColor(IndexedColors.BLUE);// 设置表格主体样式Font contentFont = new Font();contentFont.setBold(true);contentFont.setFontHeightInPoints((short) 12);contentFont.setFontName("黑体");tableStyle.setTableContentFont(contentFont);tableStyle.setTableContentBackGroundColor(IndexedColors.GREEN);return tableStyle;}

七、 自定义处理

对于更复杂的处理,EasyExcel 预留了 WriterHandler 接口来,允许你自定义处理代码:

接口中定义了三个方法:

  • sheet(): 在创建每个 sheet 后自定义业务逻辑处理;
  • row(): 在创建每个 row 后自定义业务逻辑处理;
  • cell(): 在创建每个 cell 后自定义业务逻辑处理;

我们实现了该接口后,编写自定义逻辑处理代码,然后调用 getWriterWithTempAndHandler() 静态方法获取 ExcelWriter 对象时,传入 WriterHandler 的实现类即可。

例:

ExcelWriter writer = EasyExcelFactory
.getWriterWithTempAndHandler(null, out, ExcelTypeEnum.XLSX, true, new MyWriterHandler());

八、Web 下载示例代码

public class Down {@GetMapping("/a.htm")public void cooperation(HttpServletRequest request, HttpServletResponse response) {ServletOutputStream out = response.getOutputStream();ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX, true);String fileName = new String(("UserInfo " + new SimpleDateFormat("yyyy-MM-dd").format(new Date())).getBytes(), "UTF-8");Sheet sheet1 = new Sheet(1, 0);sheet1.setSheetName("第一个sheet");writer.write0(getListString(), sheet1);writer.finish();response.setContentType("multipart/form-data");response.setCharacterEncoding("utf-8");response.setHeader("Content-disposition", "attachment;filename="+fileName+".xlsx");out.flush();}}

九、需要注意的点

1️⃣写入大数据时,需分片。比如说,从数据库中查询出数据量较大时,需要在业务层做分片处理,也就是需要分多次查询,再写入,防止内存溢出 OOM。

2️⃣Excel 最大行数问题。
Excel 03、07 版本均有行数、列数的限制:

版本               最大行            最大列
Excel 2003        65536             256
Excel 2007        1048576           16384

csv 由于是文本文件,实际上没有最大行数的限制,但是用 Excel 客户端打开还是多了不显示。也就是说,如果想写入更多的行数是不行的,强行这么做,程序会报类似如下异常:
Invalid row number (1048576) outside allowable range (0..1048575)

如何解决呢?
分多个 Excel 文件写入;
同一个 Excel 文件,分多个 Sheet 写入。

Excel 文件的生成与下载相关推荐

  1. 使用Excel文件批量生成Codabar码

    Codabar(库德巴码)是由Monarch Marking Systems在1972年研制的条形码.它是在"2 of 5"后早期阶段引入的条形码.广泛用于需要序列号的领域,如血库 ...

  2. 通过Excel文件批量生成Code 39扩展码

    code39码是条形码的一种,编码简单.能够对任意长度的数据进行编码.支持设备广泛,所以code39码是最常用的条形码之一.code39码在条码打印软件中有两种表现类型:标准code39码和支持字符更 ...

  3. 自动更新开奖数据的excel文件,供大家下载

    自动更新开奖数据的excel文件,供大家下载 2010-03-14 20:22 228492人阅读打印来源:乐彩网 作者:eren 很多人拥有自制excel电子表格,常要更新最基本的开奖信息.如有多期 ...

  4. 使用阿里的easyexcel 来实现excel文件的读取和下载

    使用阿里的easyexcel 来实现excel文件的读取和下载 1.首先下载maven依赖 <dependency><groupId>com.alibaba</group ...

  5. 获取jqGrid中的所有数据导出并生成Excel文件流供用户下载(post请求实现文件下载)...

    最近有一个需求是: 将jqGrid表格中的数据生成报表Excel文件返回给用户. 我的想法是获取jqGrid中的数据然后生成json数据,传给后端,生成文件流,响应到前端,保存为excel文件. aj ...

  6. java后台生成的Excel文件并通过浏览器下载

    java后台生成Excel文件需要引入poi, 以下是poi的pom可以引入自己的项目里面去 <dependency><groupId>org.apache.poi</g ...

  7. javaSE中的数据导出到Excel表、javaEE中后台生成Excel文件到浏览器端下载

    整个项目中导出数据到.Excel的源码 import java.io.BufferedOutputStream; import java.io.FileInputStream; import java ...

  8. springmvc html excel文件,springmvc生成文件(excel、pdf...)和文件上传

    生成文件 以下以下载excel文件为例,如有其它需要可自定义实现类继承相应springmvc提供的试图接口即可. 如生成Excel则继承AbstractExcelView,生成PDF则继承Abstra ...

  9. java 生成操作excel文件_Java生成和操作Excel文件

    AVA EXCEL API:是一开放源码项目,通过它Java开发人员可以读取Excel文件的内容.创建新的Excel文件.更新已经存在的Excel文件.使用该API非Windows操作系统也可以通过纯 ...

最新文章

  1. 合肥工业大学—SQL Server数据库实验一:数据库的创建和删除
  2. R语言dplyr包recode函数、recode_factor函数数值或因子替换实战
  3. Android测试之Monkey初体验
  4. 南外计算机课,南外小升初,怎么考?
  5. script的加载方式与执行
  6. jmeter constant timer 如何添加_阿里巴巴在开源压测工具 JMeter 上的实践和优化
  7. CodeForces 486C Palindrome Transformation 贪心+抽象问题本质
  8. JacksonUtils Jackson的JSON序列化反序列化
  9. python爬虫学习之页面登陆
  10. 在ASP.NET Identity 2.0中使用声明(Claims)实现用户组
  11. 虚拟机安装菜鸟教程(1)—CentOS6.4系统VMware安装及配置详细教程
  12. BZOJ 1041 圆上的整点 数学
  13. Appium框架中Android下EditText内容清除
  14. 给MTL库添加求行列式值
  15. Java API II
  16. vue 时间方法(yyyy-mmmm-dddd hh:mm:ss)
  17. app做好后如何上线_手机APP开发后如何上架?
  18. 基于Pytorch实现自建数据库的深度神经网络模型案例
  19. 微信壁纸小程序(SpringBoot后台V1.3.0发布)
  20. three.js绘制墙体,通过不规则路径生成墙体,3D墙体绘制

热门文章

  1. linux qt jom,Qt Creator 使用技巧之提高编译速度【使用jom参数】
  2. 钣金展开软件,包能用,无需注册,无需费用,
  3. 笔记-lxf官网面向对象高级编程
  4. 天涯明月刀服务器维护了,10月21日服务器例行维护公告
  5. 网页后缀html、htm、shtml、shtm有什么区别?
  6. 详解国产音频DAC芯片的工作原理及应用
  7. ArrayList集合的常用方法
  8. 浏览器打印,Chrome网页打印中的宽度控制
  9. 上海交通大学计算机专业培养方案,上海交通大学计算机科学与技术专业本科培养计划...
  10. Java实现--基于服务器的多用户聊天室