1 学习前言

Excel和读写和文件的读写没有本质的区别,都是属于IO操作,我们使用原生的IO就能解决Excel的导入和导出,当然操作起来比较麻烦,性能也不高,这次我们就学习poi和easyExcel(它们都属于第三方工具)的方式去基于Java和Mysql数据库导入导出我们的Excel的数据

poi:Apache

easyExcel:阿里巴巴开源的工具

2 使用常用场景

1 将大量的数据导入为excel表格

2 将excel表中的信息录入到数据库(比如习题上传)(也叫批量插入),大大减轻我们人工乃至网站的录入量

开发中经常会涉及到对Excel的处理,如导入Excel,导出Excel到数据库

目前操作excel比较流行的就是Apache POI和阿里巴巴的easyExcel

3 Apache POI

官网: https://poi.apache.org


Apache给我们提供的POI不仅可以操作excel格式,对于word,ppt,visio都是提供支持的,而且语法大概类似

HSSF是处理普通的excel的

XSSF是处理OOXML格式的excel的

值得注意点是,虽然HSSF和XSSF都可以处理excel,但是还是有差距

比如excel主要有两个版本 03版和07版,其中03版的行数最多只能存65535行,07版的是没有限制的

如果想要操作Word,那么使用HWPF

如果想要操作PPT,那么使用HSLF

如果想要操作Visio,那么使用HDGF

原生的POI使用起来比较麻烦,而且量大的时候会报OOM异常(out of memory的简称,称之为内存溢出),但是现在很多市面上的工具底层也是会使用封装POI的

4 easyExcel(阿里巴巴开源工具)

官网:https://github.com/alibaba/easyexcel

官方文档:https://www.yuque.com/easyexcel/doc/easyexcel

easyExcel是阿里巴巴开源的一个对于excel处理工具(框架)以使用简单,节省内存著称

easyExcel能大大减少内存占用的主要原因是在解析excel中没有将文件数据一次性全部加载到内存中,而是从磁盘上一行一行的读取,逐个解析
由于POI比较原生比较复杂,我们后研发出新的(工具)框架easyExcel,它是阿里巴巴退出的对于POI的后续产品,并且对POI做了升级和优化,不会因为excel数据量过大引起oom异常(内存溢出),使我们用起来更加的方便(读写Excel只需要一行代码!)

5 poi和easyExcel的不同

主要在于内存和操作过程

比如我们现在excel表有100W数据

1 poi是先把数据加载到内存,如果我们内存比较小,Java是会直接报OOM内存溢出异常,这时候就会有问题,但是easyExcel它就比较简单只能,就算读写100W行数据,会一行一行的读写,不会直接全部解析

2poi会一次性全部读取和返回execl表格的数据,但是easyExcel不会这样,它没有将文件数据一次性全部加载到内存中,而是从磁盘上一行一行的读取,逐个解析

poi和easyExcel的本质上就是时间和空间的转换,根据我们的需求自行选择即可

6 poi excel写

1 创建Java项目

2 pom引入依赖

<!--导入依赖jar包-->
<!--xls(03)-->
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.9</version>
</dependency><!--xlsx(07)-->
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>3.9</version>
</dependency><!--日期格式化工具-->
<dependency><groupId>joda-time</groupId><artifactId>joda-time</artifactId><version>2.10.1</version>
</dependency><!--单元测试-->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>
</dependency>

3 本地创建两个excel 分别是03版和07版

首先03版,它最多有65536行

但是07版的,是没有限制的

他们对应的后缀也是不一样的,03版本的xls,07版本的是xslx,这意味着操作它们的工具类也不相同.03版本用的是poi,07版用的则是poi-ooxml

4 Java的宗旨就是万物皆对象,我们也要把excel当成我们的一个对象去处理

1 工作薄
首先我们打开的excel就是一个大对象,也叫工作簿,它包括以下内容

2 工作表
每一个sheet也是我们excel对象的属性,也叫工作表,我们肯定是现有工作簿才会有工作表,而且会默认自带sheet,也可以根据我们的需要自行添加sheet工作表

3 行
excel中有很多行,每一行也是我们excel对象的属性(横的叫行,竖的叫列)

4 列
excel中有很多列,每一列也是我们excel对象的属性(横的叫行,竖的叫列)

5 单元格
一行一列有很多个单元格,每一个单元格也是我们excel对象的属性

创建我们的测试类,创建Workbook对象,按着ctrl点进去发现它是一个接口

点击箭头可以看到它的三个实现类



定义工作簿,工作表,行列,单元格,和我们手动创建excel是一样的操作,只不过是用代码来实现


03版本excel IO操作写的全部代码如下

package com.wyh.Test;import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.joda.time.DateTime;
import org.junit.Test;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Date;/*** @program: JavaExecl* @description: 写excel 03版本* @author: 魏一鹤* @createDate: 2021-12-12 10:42**/public class ExcelWrite03 {//全局路径,供我们操作使用方便static String path="D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic  void  ExcelWrite03() throws Exception {//1创建一个工作簿Workbook workbook=new HSSFWorkbook();//2创建一个工作表 工作簿里面包含工作表,所以创建工作表要通过工作簿创建//默认的工作表是没有名字的,需要我们手动赋值,和我们在excel中更改sheet工作表的名称是一样的 操作Sheet sheet=workbook.createSheet("用户表");//3创建行 行也是在我们的表中存在的,所以需要用到表来创建//默认从0开始 也就是第一行Row row1 = sheet.createRow(0);//创建单元格 第一行的第一个数据 用坐标表示为(1,1)Cell cell11 = row1.createCell(0);//创建单元格 第一行的第二个数据 用坐标表示为(1,2)Cell cell12 = row1.createCell(1);//给单元格赋值cell11.setCellValue("姓名");cell12.setCellValue("魏一鹤");//创建第二行Row row2=sheet.createRow(1);//创建第二行的第一列Cell cell21 = row2.createCell(0);Cell cell22 = row2.createCell(1);//给单元格赋值(2.1)cell21.setCellValue("出生日期");//创建时间并且格式化String s = new DateTime().toString("yyyy-MM-dd HH:mm:ss");//给单元格赋值(2.2)cell22.setCellValue(s);//生成一张表 其实就是IO流操作 03版本就是使用xls文件结尾FileOutputStream fileOutputStream = new FileOutputStream(path + "用户测试03.xls");//输出工作簿workbook.write(fileOutputStream);//关闭流fileOutputStream.close();System.out.println("用户测试03.xls生成完毕");}
}

运行后发现,会在项目本地生成我们定义的excel,打开查看



03版和07版的区别如下

1 03版本有最大长度现在 07版本没有

2 03版本后缀xls 07版本后缀xlsx

3 03版本使用的工具是HSSF,07版本使用的是XSSF

5 大数据绕导入导出(批量)

真实开发中,大多数就是大数据批量导入或者导出excel

大文件写HSSF

缺点:最多只能处理65536行,否则会报内存溢出异常

优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快

大文件写XSSF

缺点:写数据时速度非常慢,非常消耗内存,也会发生内存溢出,比如100万条

优点:可以写较大的数据量,比如20万条

03版本HSSF循环导入65536行数据(03版本最大行就是65536)

03版本HSSF循环插入65536条

package com.wyh.Test;import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.junit.Test;import java.io.FileOutputStream;/*** @program: JavaExecl* @description: 大数据量写03版本* @author: 魏一鹤* @createDate: 2021-12-14 23:31**/public class BigDateExcelWrite03 {//全局路径,供我们操作使用方便static String path = "D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void BigDateExcelWrite03() throws Exception {//开始时间 用于计算时间差long beginTime = System.currentTimeMillis();//创建工作簿  03版本使用HSSFWorkbook workbook = new HSSFWorkbook();//创建工作表 这里就不给它命令了 按照默认的来Sheet sheet = workbook.createSheet();//写入数据 循环插入65536行数据,03版的HSSF最多只能插入65536行for (int rowNum = 0; rowNum < 65536; rowNum++) {//循环创建行Row row = sheet.createRow(rowNum);for(int cellNum=0;cellNum<10;cellNum++){//循环插入列Cell cell = row.createCell(cellNum);//循环设置值cell.setCellValue(cellNum);}}System.out.println("生成excel表完毕");//03版本的后缀是xls//开启文件流FileOutputStream fileOutputStream = new FileOutputStream(path + "BigDateExcelWrite03.xlsx");//开始写excelworkbook.write(fileOutputStream);//关闭流fileOutputStream.close();//结束时间long endTime = System.currentTimeMillis();//输出花费的时间System.out.println("花费的时间:"+(double)(endTime - beginTime)/1000);}
}

运行发现excel已经创建成功,速度也非常的快

打开查看
已知03版本xls最多存65536行,那么如果我们循环插入65537行会怎么样呢? 保留源代码,循环最大值设置为65537

再次运行发现会报错

java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)

07版本XSSF循环插入65536条

把后缀改为xlsx,把HSSF缓存XSSF即可

package com.wyh.Test;import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;import java.io.FileOutputStream;/*** @program: JavaExecl* @description: 大数据量写03版本* @author: 魏一鹤* @createDate: 2021-12-14 23:31**/public class BigDateExcelWrite07 {//全局路径,供我们操作使用方便static String path = "D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void BigDateExcelWrite07() throws Exception {//开始时间 用于计算时间差long beginTime = System.currentTimeMillis();//创建工作簿 07版本的使用XSSFWorkbook workbook = new XSSFWorkbook();//创建工作表 这里就不给它命令了 按照默认的来Sheet sheet = workbook.createSheet();//写入数据 循环插入65536行数据,03版的HSSF最多只能插入65536行for (int rowNum = 0; rowNum < 65536; rowNum++) {//循环创建行Row row = sheet.createRow(rowNum);for(int cellNum=0;cellNum<10;cellNum++){//循环插入列Cell cell = row.createCell(cellNum);//循环设置值cell.setCellValue(cellNum);}}System.out.println("生成excel表完毕");//037版本的后缀是xlsx//开启文件流FileOutputStream fileOutputStream = new FileOutputStream(path + "BigDateExcelWrite07.xlsx");//开始写excelworkbook.write(fileOutputStream);//关闭流fileOutputStream.close();//结束时间long endTime = System.currentTimeMillis();//输出花费的时间System.out.println("花费的时间:"+(double)(endTime - beginTime)/1000);}
}

虽然也运行成功,但是可以明显感觉到速度不如03版HSSF,但是可以存更多的数据

打开excel查看发现数据到了65536停并没有结束,说明07版本XSSF上限不是65536,是可以存储更多的,可以写更多的数据

如果我们正在查看同一个文件,但是又进行其他操作,就会出现以下错误,我们把我们正在查看的文件关闭让它运行,等运行结束后再次打开即可

java.io.FileNotFoundException: D:\Tools\JavaWorkSpace\JavaExecl\BigDateExcelWrite07.xlsx (另一个程序正在使用此文件,进程无法访问。)

07版本XSSF导入100000条数据,把循环数改为100000即可


既然XSSF可以存这么多数据,但是速度比较慢,有没有方法可以优化效率呢(缓存,这个问题也可以叫做如何给poi加速

它就是Workbook接口三个实现类之一的SXSSFWorkbook,其他的两个我们上面都有操作过


这时候需要用到我们的SXSSF

优点:可以写非常大的数据量.如100万条甚至更多,写速度非常快,占用更少的

注意
1 过程中会产生临时文件,需要清理临时文件
2 默认由100条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件,当然缓存数量也可以自定义
3 如果自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)


SXSSF循环插入100000条数据

package com.wyh.Test;import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;import java.io.FileOutputStream;/*** @program: JavaExecl* @description: 大数据量写03版本* @author: 魏一鹤* @createDate: 2021-12-14 23:31**/public class BigDateExcelWrite07Super {//全局路径,供我们操作使用方便static String path = "D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void BigDateExcelWrite07Super() throws Exception {//开始时间 用于计算时间差long beginTime = System.currentTimeMillis();//创建工作簿 07版本的使用XSSFWorkbook workbook = new SXSSFWorkbook();//创建工作表 这里就不给它命令了 按照默认的来Sheet sheet = workbook.createSheet();//写入数据 循环插入65536行数据,03版的HSSF最多只能插入65536行for (int rowNum = 0; rowNum < 100000; rowNum++) {//循环创建行Row row = sheet.createRow(rowNum);for(int cellNum=0;cellNum<10;cellNum++){//循环插入列Cell cell = row.createCell(cellNum);//循环设置值cell.setCellValue(cellNum);}}System.out.println("生成excel表完毕");//037版本的后缀是xlsx//开启文件流FileOutputStream fileOutputStream = new FileOutputStream(path + "BigDateExcelWrite07Super.xlsx");//开始写excelworkbook.write(fileOutputStream);//关闭流fileOutputStream.close();//由于SXSSF会产生临时文件,这里我们需要清除下临时文件((SXSSFWorkbook) workbook).dispose();//结束时间long endTime = System.currentTimeMillis();//输出花费的时间System.out.println("花费的时间:"+(double)(endTime - beginTime)/1000);}
}

运行发现,excel也正常生成了,但是它(SXSSF)的速度比XSSF快的多

查看我们的super

这就是我们的临时文件,随着我们把文件的关闭,临时文件也会随之消失

7 poi excel读

1 03版poi excel读

package com.wyh.Test;import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.junit.Test;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilterInputStream;/*** @program: JavaExecl* @description: 03版poi读* @author: 魏一鹤* @createDate: 2021-12-15 23:20**/public class BigDateExcelRead03 {//全局路径,供我们操作使用方便static String path = "D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void BigDateExcelWrite03() throws Exception {//需要读取,肯定需要流 所以这边我们创建流FileInputStream filterInputStream = new FileInputStream(path+"用户测试03.xls");// 1 创建工作簿 使用excel可以完成的操作这边通过poi都可以完成//把我们的流放在工作簿用 用于读取excel数据Workbook workbook = new HSSFWorkbook(filterInputStream);//2 获取工作表  可以通过名称和下标获取工作表 这边使用下标获取 0就是第一个工作表Sheet sheet = workbook.getSheetAt(0);//行列组成单元格//3 获取行  下标为0就是第一个Row row = sheet.getRow(0);//4 获取列  下标为0就是第一个Cell cell1 = row.getCell(0);Cell cell2 = row.getCell(1);//获取单元格内容// cell  有很多方法,获取不同的数据需要用到不同的方法getStringCellValue获取字符串//读取值的时候 一定要只要数据类型 根据不同的数据类型使用不同的方法String stringCellValue = cell1.getStringCellValue();double numericCellValue = cell2.getNumericCellValue();System.out.println(stringCellValue);System.out.println(numericCellValue);//关闭流filterInputStream.close();}
}



2 07版poi excel读

只需要把引用对象(XSSF)和后缀(xlsx)改变即可

package com.wyh.Test;import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;import java.io.FileInputStream;/*** @program: JavaExecl* @description: 03版poi读* @author: 魏一鹤* @createDate: 2021-12-15 23:20**/public class BigDateExcelRead07 {//全局路径,供我们操作使用方便static String path = "D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void BigDateExcelWrite07() throws Exception {//需要读取,肯定需要流 所以这边我们创建流FileInputStream filterInputStream = new FileInputStream(path+"用户测试07.xlsx");// 1 创建工作簿 使用excel可以完成的操作这边通过poi都可以完成//把我们的流放在工作簿用 用于读取excel数据Workbook workbook = new XSSFWorkbook(filterInputStream);//2 获取工作表  可以通过名称和下标获取工作表 这边使用下标获取 0就是第一个工作表Sheet sheet = workbook.getSheetAt(0);//行列组成单元格//3 获取行  下标为0就是第一个Row row = sheet.getRow(0);//4 获取列  下标为0就是第一个Cell cell1 = row.getCell(0);Cell cell2 = row.getCell(1);//获取单元格内容// cell  有很多方法,获取不同的数据需要用到不同的方法getStringCellValue获取字符串//读取值的时候 一定要只要数据类型 根据不同的数据类型使用不同的方法String stringCellValue1 = cell1.getStringCellValue();String stringCellValue2 = cell2.getStringCellValue();System.out.println(stringCellValue1);System.out.println(stringCellValue2);//关闭流filterInputStream.close();}
}


8 读取不同的数据类型

首先准备一个多种数据类型的excel


获取全部表头信息

row.getPhysicalNumberOfCells() //获取全部的列并返回行数

获取全部表头数并且打印输出

package com.wyh.Test;import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;import javax.sql.RowSet;
import java.io.FileInputStream;/*** @program: JavaExecl* @description: 读取不同的excel数据类型* @author: 魏一鹤* @createDate: 2021-12-16 23:42**/public class ReadManyInfo {//全局路径,供我们操作使用方便static String path="D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void BigDateExcelWrite07() throws Exception {//需要读取,肯定需要流 所以这边我们创建流FileInputStream filterInputStream = new FileInputStream(path+"会员消费商品明细表.xls");// 1 创建工作簿 使用excel可以完成的操作这边通过poi都可以完成//把我们的流放在工作簿用 用于读取excel数据Workbook workbook = new HSSFWorkbook(filterInputStream);//2 获取工作表Sheet sheet = workbook.getSheetAt(0);//3获取行(表头)Row rowTitle = sheet.getRow(0); //其实这就是我们的表头 最上面的部分//判断行不为空才读if(rowTitle!=null){//如果行不为空才去读列的信息//getPhysicalNumberOfCells()获取全部的列并且返回行数int cellCount = rowTitle.getPhysicalNumberOfCells();System.out.println("cellCount = " + cellCount);for (int cellNum = 0; cellNum<cellCount; cellNum++) {//得到每一行的数据Cell cell = rowTitle.getCell(cellNum);//判断每一行是否为空 不为空再做处理if(cell!=null){//获取全部行的数据类型int cellType = cell.getCellType();//获取行的值String stringCellValue = cell.getStringCellValue();//进行输出 这里就不换行了 直接一行显示用竖线分割System.out.print(stringCellValue+"|");}}//打印完一行换行打印另外一行System.out.println();}//关闭流filterInputStream.close();}
}


打印结果和我们的excel表头内容个数是完全对的上的

获取表中内容

row.getPhysicalNumberOfCells() //获取全部的列

比较复杂的就是不同的数据类型进行判断,我们可以把这些提取成一个公用的方法


package com.wyh.Test;import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.junit.Test;import javax.sql.RowSet;
import java.io.FileInputStream;
import java.util.Date;/*** @program: JavaExecl* @description: 读取不同的excel数据类型* @author: 魏一鹤* @createDate: 2021-12-16 23:42**/public class ReadManyInfo {//全局路径,供我们操作使用方便static String path="D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void BigDateExcelWrite07() throws Exception {//需要读取,肯定需要流 所以这边我们创建流FileInputStream filterInputStream = new FileInputStream(path+"会员消费商品明细表.xls");switchDataType(filterInputStream);}public void switchDataType( FileInputStream filterInputStream ) throws Exception {// 1 创建工作簿 使用excel可以完成的操作这边通过poi都可以完成//把我们的流放在工作簿用 用于读取excel数据Workbook workbook = new HSSFWorkbook(filterInputStream);//2 获取工作表Sheet sheet = workbook.getSheetAt(0);//3获取行(表头)Row rowTitle = sheet.getRow(0); //其实这就是我们的表头 最上面的部分//判断行不为空才读if(rowTitle!=null){//如果行不为空才去读列的信息//getPhysicalNumberOfCells()获取全部的列并且返回行数int cellCount = rowTitle.getPhysicalNumberOfCells();System.out.println("cellCount = " + cellCount);for (int cellNum = 0; cellNum<cellCount; cellNum++) {//得到每一行的数据Cell cell = rowTitle.getCell(cellNum);//判断每一行是否为空 不为空再做处理if(cell!=null){//获取全部行的数据类型int cellType = cell.getCellType();//获取行的值String stringCellValue = cell.getStringCellValue();//进行输出 这里就不换行了 直接一行显示用竖线分割System.out.print(stringCellValue+"|");}}//打印完一行换行打印另外一行System.out.println();}//获取表中的内容int rowCount = sheet.getPhysicalNumberOfRows();//循环获取数据for (int rowNum = 0; rowNum < rowCount; rowNum++) {Row row = sheet.getRow(rowNum);//不为空再做处理if(row!=null){//读取行中的列    getPhysicalNumberOfCells获取全部的列int cellCount = rowTitle.getPhysicalNumberOfCells();for (int cellNum = 0; cellNum < cellCount; cellNum++) {System.out.print("["+(rowNum+1)+"-"+(cellNum + 1)+"]");//获取数据Cell cell = row.getCell(cellNum);//因为不知道列的数据类型 所以这里我们要匹配数据类型//如果不为空if(cell != null){//获取类型int cellType = cell.getCellType();String cellValue="";//判断cell的数据类型switch (cellType) {case HSSFCell.CELL_TYPE_STRING://字符串System.out.print("【STRING】");cellValue = cell.getStringCellValue();break;case HSSFCell.CELL_TYPE_BOOLEAN://布尔System.out.print("【BOOLEAN】");cellValue = String.valueOf(cell.getBooleanCellValue());break;case HSSFCell.CELL_TYPE_BLANK://空System.out.print("【BLANK】");break;case HSSFCell.CELL_TYPE_NUMERIC:System.out.print("【NUMERIC】");//cellValue = String.valueOf(cell.getNumericCellValue());if (HSSFDateUtil.isCellDateFormatted(cell)) {//日期System.out.print("【日期】");Date date = cell.getDateCellValue();cellValue = new DateTime(date).toString("yyyy-MM-dd");} else {// 不是日期格式,则防止当数字过长时以科学计数法显示System.out.print("【转换成字符串】");cell.setCellType(HSSFCell.CELL_TYPE_STRING);cellValue = cell.toString();}break;case Cell.CELL_TYPE_ERROR:System.out.print("【数据类型错误】");break;}System.out.println(cellValue);}}}}//关闭流filterInputStream.close();}
}

9 计算公式

首先需要准备一个excel

需要获取计算公式eval FormulaEvaluator


如果我们用没有计算公式的行,那么不会有用


package com.wyh.Test;import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.junit.Test;import java.io.FileInputStream;
import java.io.FileNotFoundException;/*** @program: JavaExecl* @description: excel计算公式* @author: 魏一鹤* @createDate: 2021-12-18 00:03**/public class ExcelCalculation {//全局路径,供我们操作使用方便static String path="D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void  testFormula() throws Exception {//首先还是要获取路径FileInputStream fileInputStream = new FileInputStream(path + "计算公式.xls");//读取文件Workbook workbook = new HSSFWorkbook(fileInputStream);//读取表Sheet sheet = workbook.getSheetAt(0);//根据表拿出数据//因为我们的需要计算的单元格在第五行 从0开始的 所以下标为4//如果我们用没有计算公式的行,那么不会有用Row row = sheet.getRow(4);//(第一行 4,1)Cell cell = row.getCell(0);//拿到计算公式 evalFormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook)workbook);//输出单元格的内容//获取类型进行便利int cellType = cell.getCellType();switch (cellType){case Cell.CELL_TYPE_FORMULA://获取公式String cellFormula = cell.getCellFormula();System.out.println(cellFormula);//计算CellValue evaValue=formulaEvaluator.evaluate(cell);String cellValue = evaValue.formatAsString();System.out.println(cellValue);//结束循环break;}}}

10 easyExcel写

特点:阿里巴巴开源的工具,代码简化,占用内存少,优化OOM(内存溢出异常)

1 pom引入依赖
由于pom底层也是使用的poi进行处理,为了避免冲突,我们把pom引入的pom注释掉

<!--easyExcel依赖-->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.0-beta2</version>
</dependency><!--导入依赖jar包-->
<!--xls(03)-->
<!--<dependency>--><!--<groupId>org.apache.poi</groupId>--><!--<artifactId>poi</artifactId>--><!--<version>3.9</version>-->
<!--</dependency>--><!-- xlsx(07) -->
<!--<dependency>--><!--<groupId>org.apache.poi</groupId>--><!--<artifactId>poi-ooxml</artifactId>--><!--<version>3.9</version>-->
<!--</dependency>-->

2 准备一个实体类 因为easyexcel根据实体类自动生成的表 效率非常的高

package com.wyh.entity;import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;import java.util.Date;/*** @program: JavaExecl* @description: easyExcel实体类 可以用它生成excel* @author: 魏一鹤* @createDate: 2021-12-18 23:03**/
@Data
public class DemoData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;/*** 忽略这个字段*/@ExcelIgnoreprivate String ignore;
}

它对应生成的就是这样


3 写一个基本的写入操作

//根据list写入excel
@Test
public void simpleWrite() {// 写法1 JDK8+// since: 3.0.0-beta1//生成的文件名和文件所在位置String fileName =path+"easyExcel.xlsx";//开始写入 这里说下几个参数//1 fileName 是一个io流 自动生成excel//2 DemoData.class根据哪个类的规则去生成excel//3 sheetName 生成的表的名字//4 进行写出,写入到规则就是我们上面定义的方法循环EasyExcel.write(fileName,DemoData.class).sheet("模板").doWrite(data());
}

完整的代码

package com.wyh.Test;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.wyh.entity.DemoData;
import org.apache.commons.collections4.ListUtils;
import org.junit.Test;import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** @program: JavaExecl* @description: easyExcel写操作* @author: 魏一鹤* @createDate: 2021-12-18 23:08**/public class EasyExcelWrite {//全局路径,供我们操作使用方便static String path="D:\\Tools\\JavaWorkSpace\\JavaExecl\\";private List<DemoData> data() {List<DemoData> list = new ArrayList<DemoData>();for (int i = 0; i < 10; i++) {DemoData data = new DemoData();data.setString("字符串" + i);data.setDate(new Date());data.setDoubleData(0.56);list.add(data);}return list;}//根据list写入excel@Testpublic void simpleWrite() {// 写法1 JDK8+// since: 3.0.0-beta1//生成的文件名和文件所在位置String fileName =path+"easyExcel.xlsx";//开始写入 这里说下几个参数//1 fileName 是一个io流 自动生成excel//2 DemoData.class根据哪个类的规则去生成excel//3 sheetName 生成的表的名字//4 进行写出,写入到规则就是我们上面定义的方法循环EasyExcel.write(fileName,DemoData.class).sheet("模板").doWrite(data());}
}

运行完毕之后发现已经生成了,而且和我们想要的数据格式是一样的

11 easyExcel读

1 需要一个实体,我们已经有了,可以直接用上面的DemoData

package com.wyh.entity;import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;import java.util.Date;/*** @program: JavaExecl* @description: easyExcel实体类 可以用它生成excel* @author: 魏一鹤* @createDate: 2021-12-18 23:03**/
@Data
public class DemoData {@ExcelProperty("字符串标题")private String string;@ExcelProperty("日期标题")private Date date;@ExcelProperty("数字标题")private Double doubleData;/*** 忽略这个字段*/@ExcelIgnoreprivate String ignore;
}

2 需要一个监听器

package com.wyh.Test;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.CellExtra;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.fastjson.JSON;
import com.wyh.entity.DemoData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** @program: JavaExecl* @description: easyExcel监听器* @author: 魏一鹤* @createDate: 2021-12-18 23:30**/
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener<DemoData> {private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);/*** 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 5;List<DemoData> list = new ArrayList<DemoData>();/*** 假设这个是一个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;}/*** 这个每一条数据解析都会来调用** @param data*            one row value. Is is same as {@link AnalysisContext#readRowHolder()}* @param context*/@Overridepublic void invoke(DemoData data, AnalysisContext context) {System.out.println(JSON.toJSONString(data));list.add(data);System.out.println(list);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (list.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listlist.clear();}}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库saveData();LOGGER.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {demoDAO.save(list);}
}

3 然后写一个基本的读操作

package com.wyh.Test;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.fastjson.JSON;
import com.wyh.entity.DemoData;
import org.apache.commons.collections4.ListUtils;
import org.junit.Test;import java.io.File;
import java.util.List;/*** @program: JavaExecl* @description: excel读数据* @author: 魏一鹤* @createDate: 2021-12-18 23:38**/public class EasyExcelRead {//全局路径,供我们操作使用方便static String path="D:\\Tools\\JavaWorkSpace\\JavaExecl\\";@Testpublic void simpleRead() {//文件路径String fileName = path + "easyExcel.xlsx";//重点注意监听器读取的逻辑//fileName 文件//DemoData 类规则//DemoDataListener 监听器//sheet().doRead() 表的读取EasyExcel.read(fileName,DemoData.class,new DemoDataListener()).sheet().doRead();}}

启动发现全部数据被正常读取到

4 总结下easyExcel的步骤

1 写入 根据固定的类格式进行写入
2 读取 根据监听器设置规则进行读取

本次poi和easyExcel操作excel先到此为止,后续会结合项目写导入导出数据库的真实例子

poi和easyExcel基于Java操作Excel学习笔记相关推荐

  1. C++操作Excel学习笔记

    C++操作Excel学习笔记 一: [当前博文转载自http://blog.csdn.net/fullsail/article/details/4067416] C++读取Excel文件方式比较 C+ ...

  2. 安装命令:pip install xlrd ,pandas操作Excel学习笔记__7000

    pandas操作Excel学习笔记_loc和iloc_7000 pandas操作Excel学习笔记__7000 1.安装环境:pandas需要处理Excel的模块xlrd,所以需要提前安装xlrd.不 ...

  3. python连接excel存放数据_有了这篇python操作Excel学习笔记,以后储存数据 办公很轻松!...

    最近在做一些数据处理和计算的工作,因为数据是以.csv格式保存的,因此刚开始直接用Excel来处理.但是做着做着发现重复的劳动其实并没有多大的意义,于是就想着写个小工具帮着处理.以前正好在一本书上看到 ...

  4. Python 操作 Excel 学习笔记

    Python 操作 Excel 文章目录 Python 操作 Excel 用 xlrd 模块读取 Excel xlrd 安装 xlrd 常用函数 打开,加载工作簿 获取工作表 Excel 行操作与列操 ...

  5. 用python处理excel的基本语法_Python操作Excel学习笔记(28):条件格式

    excelperfect Excel支持3种不同类型的条件格式:内置.标准和自定义.内置条件格式将特定规则与预定义样式相组合.标准条件格式将特定规则与自定义格式相组合.此外,可以定义自定义公式来应用使 ...

  6. 基于Java的Selenium学习笔记——Assert

    用assert来判断一个表达式,返回true或者false,若表达式返回false,则会导致AssertionError. 注意Eclipse中assert默认是关闭的,需手动开启,如下图: 第一个简 ...

  7. 基于Java的Selenium学习笔记——启动Chrome

    用Selenium 打开Chrome浏览器,需下载对应的chromedriver.exe,并将chromedriver放到lib文件夹下. 打开浏览器的代码如下: package com.seleni ...

  8. Java操作Excel三种方式POI、Hutool、EasyExcel

    Java操作Excel三种方式POI.Hutool.EasyExcel 1. Java操作Excel概述 1.1 Excel需求概述 1.2 Excel操作三种方式对比 2. ApachePOIExc ...

  9. java excel api 下载文件_Java-Excel Java操作Excel POI(Jakarta POI API) - 下载 - 搜珍网

    Java操作Excel/Jakarta POI API/data/Jakarta POI API.doc Java操作Excel/Jakarta POI API/jar/poi-3.0.2-FINAL ...

最新文章

  1. OpenCV成长之路:图像滤波
  2. linux img 内核启动,linux的启动流程(initrd.img)
  3. 【渝粤教育】广东开放大学 会展项目管理 形成性考核 (59)
  4. 项目入口_新进展!石家庄地铁项目长安公园站出入口全部封顶
  5. 电力物联网智慧路灯充电桩传感器技术应用方案
  6. weblogic占用java_weblogic下java程序占用cpu过高的问题排查
  7. 飞信2016 5.6.8820.0超级精简版
  8. echarts 地图增加背景图
  9. linux 检查ntp版本,查看linux安装ntp服务器配置
  10. win10隐藏网络计算机,Win10怎么连接隐藏的wifi?
  11. 【精读论文】2015-BMVC-Learning Deep Representations of Appearance and Motion for Anomalous Event Detection
  12. hololens 播放video
  13. 详解vue原理之观察模式Dep->Watcher
  14. R语言使用t.test函数执行t检验获取总体平均值的置信区间(默认输出结果包括95%置信水平的置信区间)
  15. 编译原理:什么是编译程序?
  16. 创建反向词典,为你打开神经网络的大门
  17. 我的专业计算机作文说明文,我的电脑作文说明文
  18. windows10无法搜索计算机,win10搜索功能失效用不了如何解决|win10搜索功能搜不了文件的解决方法...
  19. java反射获取字段的顺序6_反射之获取字段
  20. 笔记本 i3 i5 i7 区别

热门文章

  1. qq批量登录软件_把微信PC版越甩越远:QQ电脑版这些新功能太良心
  2. 微观:心流,宏观:ikigai
  3. 无人机满世界惹祸 各国纷纷立法应对
  4. Pico4VR一体机游戏资源下载安装教程,Pico4无线串流PCVR游戏教程pico4串流游戏下载
  5. ElasticaSearch安装以及配置
  6. jQuery学习第二天——jQuery的常见效果(上)
  7. 【原创】新韭菜日记18--学习股市发展趋势,牛市回顾,券商分析
  8. iOS开发中一些有用的小代码
  9. DOS命令dir是否能查看隐藏文件夹
  10. 真正的 IT 男是什么样的?