POI和EasyExcel讲解全文内容来自官方文档和相关视频的讲解,本作者用做笔记!
- 常用场景:
- Apache POI
- Apache POI 官网:
- 基本功能:
- Excel 版本区别:03版和07版
- POI-Excel 写
- 创建普通Maven项目:
- 创建完项目,导入依赖 pom.xml:
- Java编程,万物皆对象:
- 实现类,写入
- 大文件写入HSSF (07版)
- 大文件写SXSSF (比07版,速度更快)
- POI-Excel 读
- 03版本,读数据:
- 07版本,读数据:
- 读取不同数据类型:(最麻烦)注意类型转换
- 读取并计算公式:
- EasyExcel
- EasyExcel:快速、简洁、解决大文件内存溢出的java处理Excel工具
- POI一般底层仍会使用,弊端就是当数据量大的时候会报OOM异常,相比较POI相对简单一些!
- 官方文档:
- pom.xml 依赖
- 实体类:EasyExcel会根据实体类自动生成表
- 实现类:写数据
- 实现类:读数据
- 持久层:DAO
- DemoDataListener:
- 将用户信息导出为Excel表格(导出xls数据…)
- 将Excel表中的信息录入到网站数据库(习题上传…公司找老师填写题目到excel再上传网站,网站会利用相关POI将数据回填到数据库,这样大大减轻网站录入量。)
开发中经常会设计到excel的处理,如导出Excel,导入Excel到数据库中!
*** 作Excel目前流行的就是Apache POI 和 阿里巴巴 EasyExcel!
Apache POI Apache POI 官网:
Apache POI 官网:https://poi.apache.org/
Apache POI 是Apache软件基金会的开放源码函数库,用Java编写的免费开源的跨平台的 Java API。Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。
基本功能:HSSF - 提供读写Microsoft Excel XLS格式档案的功能。
XSSF - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
HWPF - 提供读写Microsoft Word DOC格式档案的功能。
HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
HDGF - 提供读Microsoft Visio格式档案的功能。
HPBF - 提供读Microsoft Publisher格式档案的功能。
HSMF - 提供读Microsoft Outlook格式档案的功能。
03版本 2003版本.xls
:行数只有65535行,多了放不了。
07版本 2007版本.xlsx
:无限制
POI-Excel 写 创建普通Maven项目:
Java版本:1.8
<dependencies>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
<version>3.9version>
dependency>
<dependency>-->
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>3.9version>
dependency>
<dependency>
<groupId>joda-timegroupId>
<artifactId>joda-timeartifactId>
<version>2.10.1version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
dependency>
dependencies>
Java编程,万物皆对象:
excel 表中的各个对象: 工作簿、工作表、行、列、单元格
先创建工作簿,后创建工作表,再有行和列,最后组成了数据:
public class ExcelWriteTest { //03版本实现类
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;//设置固定文件路径
@Test //创建03版本
public void testWrite03() throws Exception{
// 1.创建一个工作簿
Workbook workbook = new HSSFWorkbook(); //对象存在差别 HSSF
// 2.创建一个工作表
Sheet sheet = workbook.createSheet("guo1"); //sheet 名字为:guo1
// 3.创建一个行 (1,1)
Row row11 = sheet.createRow(0); //第一行
// 4. 创建一个单元格
Cell cell11 = row11.createCell(0); //第一行,第一列的格子(0,0)
cell11.setCellValue("今日人数"); //设置值
//(1,2)
Row row12 = sheet.createRow(1);
Cell cell12 = row12.createCell(0);
cell12.setCellValue(666);
// 第二行
Row row2 = sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("统计时间");
// (2,2)
Cell cell22 = row2.createCell(1);
String time = new DateTime().toString("yyyy-mm-dd HH:mm:ss");
cell22.setCellValue(time);
//生成一张表(IO流) 03版本 就是使用xls结尾!
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "03版本统计表.xls");
//输出
workbook.write(fileOutputStream);
//关闭流
fileOutputStream.close();
System.out.println("03版本统计表.xls 生成完毕!");
}
}
public class ExcelWriteTest { //07版本实现类 和03版本写在同一个类下
//设置固定文件路径,与03相同
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;
@Test //创建07版本
public void testWrite07() throws Exception{
// 1.创建一个工作簿
Workbook workbook = new XSSFWorkbook(); //对象存在差别 XSSF
// 2.创建一个工作表
Sheet sheet = workbook.createSheet("guo2"); //sheet 名字为:guo1
// 3.创建一个行 (1,1)
Row row11 = sheet.createRow(0); //第一行
// 4. 创建一个单元格
Cell cell11 = row11.createCell(0); //第一行,第一列的格子(0,0)
cell11.setCellValue("今日人数"); //设置值
//(1,2)
Row row12 = sheet.createRow(1);
Cell cell12 = row12.createCell(0);
cell12.setCellValue(666);
// 第二行
Row row2 = sheet.createRow(1);
Cell cell21 = row2.createCell(0);
cell21.setCellValue("统计时间");
// (2,2)
Cell cell22 = row2.createCell(1);
String time = new DateTime().toString("yyyy-mm-dd HH:mm:ss");
cell22.setCellValue(time);
//生成一张表(IO流) 03版本 就是使用xlsx结尾! //这里存在差别
FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07版本统计表.xlsx");
//输出
workbook.write(fileOutputStream);
//关闭流
fileOutputStream.close();
System.out.println("07版本统计表.xlsx 生成完毕!")
}
}
03与07差别只有文件后缀不同!
数据批量写入:大文件写入HSSF (03版)
缺点:最多只能处理65536行,否则会抛出异常
java.lang.11legalArgumentException : Invalid row number (65536) outside allowable range (0..65535)
优点:过程中写入缓存,不 *** 作磁盘,最后一次性写入磁盘,速度快。
//设置固定文件路径,与03相同
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;
@Test //03大量数据
public void testWrite03BigData() throws IOException {
//时间差 begin 和 end 的差值
long begin = System.currentTimeMillis();
// 创建工作簿
Workbook workbook = new HSSFWorkbook(); //03版本对象 HSSF
// 创建工作表
Sheet sheet = workbook.createSheet();
// 写入数据
for( int rowNum = 0; rowNum < 65536; rowNum++){ //65537会报错
Row row = sheet.createRow(rowNum); // 行
for (int cellNum = 0; cellNum <10; cellNum++){ //这里设置了10列
Cell cell = row.createCell(cellNum); // 列
cell.setCellValue(cellNum); //循环65536行,每一行都是10列的数据
}
}
//运行完毕,输出文件流
System.out.println("over");
FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite03BigDATA.xls" );
workbook.write(outputStream); //写入
// 关闭流
outputStream.close();
//时间差
long end = System.currentTimeMillis();
//计算时间差,强制转换double,按秒来
System.out.println((double)(end - begin)/1000);
}
大文件写入HSSF (07版)
缺点: 写数据时速度非常慢,非常耗内存,可能会发生内存溢出,如100万条。
优点,可以写入较大的数据量,如20万条数据。
//设置固定文件路径,与03相同
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;
@Test //07大数据读取 耗时较长
public void testWrite07BigData() throws IOException {
//时间差 begin 和 end 的差值
long begin = System.currentTimeMillis();
// 创建工作簿
Workbook workbook = new XSSFWorkbook(); //07版本对象 XSSF
// 创建工作表
Sheet sheet = workbook.createSheet();
// 写入数据
for( int rowNum = 0; rowNum < 1000000; rowNum++){
Row row = sheet.createRow(rowNum); // 行
for (int cellNum = 0; cellNum <10; cellNum++){ //这里设置了10列
Cell cell = row.createCell(cellNum); // 列
cell.setCellValue(cellNum); //循环65536行,每一行都是10列的数据
}
}
//运行完毕,输出文件流
System.out.println("over");
FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite07BigDATA.xlsx" );
workbook.write(outputStream); //写入
// 关闭流
outputStream.close();
//时间差
long end = System.currentTimeMillis();
//计算时间差,强制转换double,按秒来
System.out.println((double)(end - begin)/1000);
}
大文件写SXSSF (比07版,速度更快)
优点:可以写非常大的数据量,如100万条甚至更多条,写数据速度快,占用更少的内存。
注意:过程中会产生临时文件,需要清理临时文件
原理:默认由100条记录保存在内存中,如果超出这数量,则最前面的数据最前面的数据被写入临时文件,如果想自定义内存中的数据的数量,可以使用new SXSSFWorkbook(数量)
SXSSFWorkbook 来至官方的解释,实现“BigGridDemo“策略的流式XSSFWorkbook版本,这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。
请注意,仍然可能会消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注释,仍然只能存储在内存中,因此如果广泛使用,可能需要大量内存。
//设置固定文件路径,与03相同
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/data/" ;
@Test //07大数据读取 耗时短
public void testWrite07BigDataS() throws IOException{
//时间差 begin 和 end 的差值
long begin = System.currentTimeMillis();
// 创建工作簿
Workbook workbook = new SXSSFWorkbook(); //07版本对象 XSSF
// 创建工作表
Sheet sheet = workbook.createSheet();
// 写入数据
for( int rowNum = 0; rowNum < 100000; rowNum++){
Row row = sheet.createRow(rowNum); // 行
for (int cellNum = 0; cellNum <10; cellNum++){ //这里设置了10列
Cell cell = row.createCell(cellNum); // 列
cell.setCellValue(cellNum); //循环65536行,每一行都是10列的数据
}
}
//运行完毕,输出文件流
System.out.println("over");
FileOutputStream outputStream = new FileOutputStream(PATH + "testWrite07BigDATAS.xlsx" );
workbook.write(outputStream); //写入
// 关闭流
outputStream.close();
//清除临时文件!
((SXSSFWorkbook)workbook).dispose();
//时间差
long end = System.currentTimeMillis();
//计算时间差,强制转换double,按秒来
System.out.println((double)(end - begin)/1000);
}
POI-Excel 读
// workbook 代表工作簿,使用excel能 *** 作的功能,workbook都可以 *** 作
workbook.createCellStyle();//创建样式
workbook.createName();//创建名称
workbook.findFont();//找内容
workbook.getAllPictures();//得到一些图片
workbook.isHidden();//隐藏
workbook.removeName();//移除名字
//sheet 代表表中的设置 使用excel表能 *** 作的功能,sheet都可以 *** 作
sheet.createRow();//创建行
sheet.autoSizeColumn();//列的大小
sheet.getColumnWidth();//列的宽度
sheet.getDefaultRowHeightInPoints();//行间距
03版本,读数据:
@Test。 //03版本读数据
public void testRead03() throws Exception{
//获取文件流
FileInputStream inputStream = new FileInputStream(PATH +"guo_poi03版本统计表.xls");
// 1.创建一个工作簿 使用excel能 *** 作的功能,workbook都可以 *** 作
Workbook workbook = new HSSFWorkbook(inputStream); // 流获取到工作簿
// 2.得到表 表中的设置
Sheet sheet = workbook.getSheetAt(0); //拿去 get方法
// 3.得到行
Row row = sheet.getRow(0); //得到第一行
// 4.得到列
Cell cell = row.getCell(0); //得到第一列
//读取值时需要类型判断!
// 获得字符串
System.out.println(cell.getStringCellValue()); //字符串类型
//cell.getNumericCellValue()获取数字
// 关闭流
inputStream.close();
}
07版本,读数据:
@Test //07版本读数据
public void testRead07() throws Exception{
//获取文件流
FileInputStream inputStream = new FileInputStream(PATH +"guo_poi07版本统计表.xlsx");
// 1.创建一个工作簿 使用excel能 *** 作的功能,workbook都可以 *** 作
Workbook workbook = new XSSFWorkbook(inputStream); // 流获取到工作簿
// 2.得到表 表中的设置
Sheet sheet = workbook.getSheetAt(0); //拿去 get方法
// 3.得到行
Row row = sheet.getRow(0); //得到第一行
// 4.得到列
Cell cell = row.getCell(0); //得到第一列
//读取值时需要类型判断!
// 获得字符串
System.out.println(cell.getNumericCellValue()); //获取数字
// 关闭流
inputStream.close();
}
注意获取值的类型;
读取不同数据类型:(最麻烦)注意类型转换 @Test //测试类型
public void testCellType() throws Exception{
//获取文件流
FileInputStream inputStream = new FileInputStream(PATH +"会员消费商品明细表.xls");
// 创建一个工作簿 使用excel能 *** 作的这边都可以 *** 作
Workbook workbook = new HSSFWorkbook(inputStream); //03版本
//获取表
Sheet sheet = workbook.getSheetAt(0);
//获取标题内容
Row rowTitle = sheet.getRow(0); //最上面一行
if(rowTitle != null){
//一定要掌握 拿到所有的列
int cellCount = rowTitle.getPhysicalNumberOfCells(); // 获取所有列数据,总数 比如:13个列
for (int cellNum = 0; cellNum < cellCount;cellNum++){
Cell cell = rowTitle.getCell(cellNum);
if (cell != null){
CellType cellType = cell.getCellType(); //取出数据的类型
String cellValue = cell.getStringCellValue(); //获取数据 该数据是string 字符串类型
System.out.print(cellValue + "|"); //把数据打印出来,输出时不换行
}
}
System.out.println();
}
//获取表中的内容
int rowCount = sheet.getPhysicalNumberOfRows(); //获取所有的行 getPhysicalNumberOfCells()获取所有列
for( int rowNum = 1;rowNum< rowCount;rowNum++){
Row rowData = sheet.getRow(rowNum);
if(rowData != null){ //判空
//读取列
int cellCoumt = rowTitle.getPhysicalNumberOfCells();
for (int cellNum = 0;cellNum < cellCoumt ; cellNum++){
System.out.print("["+(rowNum+1)+"-"+(rowNum+1)+"]");
Cell cell = rowData.getCell(cellNum);
// 匹配列的数据类型
if(cell != null){
CellType cellType = cell.getCellType();
String cellValue = "";
switch(cellType){
case STRING: //字符串类型
System.out.print("[String]");
cellValue = cell.getStringCellValue();
break;
case BOOLEAN: //布尔类型
System.out.print("[BOOLEAN]");
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case BLANK: //空
System.out.print("[BLANK]");
break;
case _NONE:
System.out.print("[NONE]");
break;
case NUMERIC: //数字(日期、普通数字)
System.out.print("[NUMERIC]");
if(DateUtil.isCellInternalDateFormatted(cell)) { //日期
System.out.print("[日期]");
Date date = cell.getDateCellValue();
cellValue = new DateTime(date).toString("yyyy-MM-dd");
}else{
//不是日期格式,防止数字过长!
System.out.print("[转换为字符串输出]");
cellValue = cell.toString();
}
break;
case ERROR: //错误
System.out.print("【数据类型错误】");
break;
}
System.out.println(cellValue);
}
}
}
}
inputStream.close(); //关闭流
}
@Test //测试类型 提取成工具类
public void testCellType(FileInputStream inputStream) throws Exception{
// 创建一个工作簿 使用excel能 *** 作的这边都可以 *** 作
Workbook workbook = new HSSFWorkbook(inputStream); //03版本
//获取表
Sheet sheet = workbook.getSheetAt(0);
//获取标题内容
Row rowTitle = sheet.getRow(0); //最上面一行
if(rowTitle != null){
//一定要掌握 拿到所有的列
int cellCount = rowTitle.getPhysicalNumberOfCells(); // 获取所有列数据,总数 比如:13个列
for (int cellNum = 0; cellNum < cellCount;cellNum++){
Cell cell = rowTitle.getCell(cellNum);
if (cell != null){
CellType cellType = cell.getCellType(); //取出数据的类型
String cellValue = cell.getStringCellValue(); //获取数据 该数据是string 字符串类型
System.out.print(cellValue + "|"); //把数据打印出来,输出时不换行
}
}
System.out.println();
}
//获取表中的内容
int rowCount = sheet.getPhysicalNumberOfRows(); //获取所有的行 getPhysicalNumberOfCells()获取所有列
for( int rowNum = 1;rowNum< rowCount;rowNum++){
Row rowData = sheet.getRow(rowNum);
if(rowData != null){ //判空
//读取列
int cellCoumt = rowTitle.getPhysicalNumberOfCells();
for (int cellNum = 0;cellNum < cellCoumt ; cellNum++){
System.out.print("["+(rowNum+1)+"-"+(rowNum+1)+"]");
Cell cell = rowData.getCell(cellNum);
// 匹配列的数据类型
if(cell != null){
CellType cellType = cell.getCellType();
String cellValue = "";
switch(cellType){
case STRING: //字符串类型
System.out.print("[String]");
cellValue = cell.getStringCellValue();
break;
case BOOLEAN: //布尔类型
System.out.print("[BOOLEAN]");
cellValue = String.valueOf(cell.getBooleanCellValue());
break;
case BLANK: //空
System.out.print("[BLANK]");
break;
case _NONE:
System.out.print("[NONE]");
break;
case NUMERIC: //数字(日期、普通数字)
System.out.print("[NUMERIC]");
if(DateUtil.isCellInternalDateFormatted(cell)) { //日期
System.out.print("[日期]");
Date date = cell.getDateCellValue();
cellValue = new DateTime(date).toString("yyyy-MM-dd");
}else{
//不是日期格式,防止数字过长!
System.out.print("[转换为字符串输出]");
cellValue = cell.toString();
}
break;
case ERROR: //错误
System.out.print("【数据类型错误】");
break;
}
System.out.println(cellValue);
}
}
}
}
inputStream.close(); //关闭流
}
读取并计算公式:
表格样式:
@Test //公式
public void testFormula() throws Exception{
//获取文件流
FileInputStream inputStream = new FileInputStream(PATH +"公式.xls");
// 创建一个工作簿 使用excel能 *** 作的这边都可以 *** 作
Workbook workbook = new HSSFWorkbook(inputStream); //03版本
//获取表
Sheet sheet = workbook.getSheetAt(0);
//获取第五行
Row row = sheet.getRow(4);
//获取第一列
Cell cell = row.getCell(0);
//拿到计算公式 eval函数 通过它来计算
FormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook)workbook);
//输出单元格内容
CellType cellType = cell.getCellType();
switch (cellType){
case FORMULA: //公式类型 需要和公式类型匹配,如果不匹配不会进入方法的
String formula = cell.getCellFormula();
System.out.println(formula);
//计算
CellValue evaluate = formulaEvaluator.evaluate(cell); // 用evaluate(cell)方法计算
String cellValue = evaluate.formatAsString();//计算结果,列的值,格式化成字符串来输出
System.out.println(cellValue); //输出结果
break;
}
}
EasyExcel
EasyExcel:快速、简洁、解决大文件内存溢出的java处理Excel工具
POI一般底层仍会使用,弊端就是当数据量大的时候会报OOM异常,相比较POI相对简单一些!
EasyExcel :
EasyExcel官方地址:https://github.com/alibaba/easyexcel
官方简介:
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便
GitHub EasyExcel :
官方文档:https://www.yuque.com/easyexcel/doc/easyexcel
pom.xml 依赖EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存存储著称。
EasyExcel 能大大减少占用内存的主要原因是在解析Excel是没有将数据一次性全部加载到内存中,而且是从磁盘上一行行读取数据,逐个解析。
内存问题:POI = 100w先加载到内存中(十分耗内存)再一次性写入文件中,当内存不够时可能会报OOM。而EasyExcel在写入文件中是通过磁盘一行一行读取的。
下图是EasyExcel和POI在解析Excel时的对比图。
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>3.0.5version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.22version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.62version>
dependency>
实体类:EasyExcel会根据实体类自动生成表
package easy;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;
@Data
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
实现类:写数据
package easy;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.ListUtils;
import org.junit.Test;
import java.util.Date;
import java.util.List;
public class TestExcel {
String PATH = "/Users/**/Documents/IdeaProjects/guo_poi/src/main/resources/" ;//设置固定文件路径
//自动生成10个随机数,再把随机数放入list
private List<DemoData> data() { //实体类加载过来
List<DemoData> list = ListUtils.newArrayList();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
//设初值,返回到list中
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
//根据list 写入excel
/**
* 最简单的写
*
* 1. 创建excel对应的实体对象 参照{@link DemoData}
*
* 2. 直接写即可
*/
@Test
public void simpleWrite() {
// 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入
// 最简单写法 JDK8+ 其余写法官方文档上都有
String fileName = PATH + "dataEasyTest.xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// write.(fileName,格式类) 格式类会根据注释自动生成表的属性
// sheet("表名") 生成表的方法
//dowrite.(数据) 写入数据 从前端或者数据库读取的数据
EasyExcel.write(fileName, DemoData.class) //filename 数据流,DemoData.class 对应实体 标题生成
.sheet("模板") //生成一个sheet为模版的表
.doWrite(data());//dowrite(数据)
//data() 这里就是写了一个简单的生成随机数,真实情况是从前端返过来或数据库中读取出来的一个list,绑定mapper就行
}
}
成功运行:可以看到生成了dataEasyExcel.xlsx
生成的数据
//只写了方法,该方法也放在TestExcel中
/**
* 最简单的读
*
* 1. 创建excel对应的实体对象 参照{@link DemoData}
*
* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
*
* 3. 直接读即可
*/
@Test
public void simpleRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 最简单写法 JDK8+ 其余写法官方文档上都有
String fileName = PATH + "dataEasyExcel.xlsx";
// 这里 需要指定读哪个文件,用哪个class按格式输出,通过DemoDataListener监视器去判断逻辑然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
持久层:DAO
package easy;
import java.util.List;
/**
* 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
**/
public class DemoDAO {
public void save(List<DemoData> list) {
//持久化 *** 作,保存数据库!
// 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
DemoDataListener:
package easy;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener extends AnalysisEventListener<DemoData> {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
private List<DemoData> list = new ArrayList<DemoData>();
//sprin 需要注入时使用
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
//读取数据时会执行 invoke 方法
// 读入
// DemoData 类型
// AnalysisContext 分析器 用来分析上下文
/**
* 这个每一条数据解析都会来调用 invoke 方法
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
//fastjson 导入Maven依赖
System.out.println(JSON.toJSONString(data)); //读取到的每一条数据都会变成 JSON字符串
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData(); //保存数据库 这里是空的
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list);
log.info("存储数据库成功!");
}
}
固定套路:
1. 写入,固定类格式的进行写入!
2. 读取,根据监听器设置的规则进行读取!
一切代码都在官方文档
官方文档:
官方文档:https://www.yuque.com/easyexcel/doc/easyexcel
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)