POI
什么是POI
POI是Apache软件基金会用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。
所以POI的主要功能是可以用Java操作Microsoft Office的相关文件,但是一般我们都是用来操作Excel相关文件。
POI使用场景
- 将数据库信息导出为Excle表格(俗称:导出数据)
- 将Excel格式的文件导入到数据库里(俗称:导入数据)
操作Excel目前比较流行的就是Apache POI和阿里的easyExcel
基本功能
HSSF — 提供读写Microsoft Excle格式档案的功能。 03版本
XSSF — 提供读写Microsoft Excle OOXML格式档案的功能。 07版本
HWPF — 提供读写Microsoft Word格式档案的功能。
HSLF — 提供读写Microsoft PowerPoint格式档案的功能。
HDGF — 提供读写Microsoft Visio格式档案的功能。
Excle 03 版本和 07 版本的区别
①03版的excle只能放65536条,而07版本的没有限制
②后缀也不一样,所以操作的工具类也不同
工作簿,工作表,行,列
POI官网:http://poi.apache.org/index.html
POI的使用
首先加入POI的依赖和测试工具依赖
<dependencies>
<!-- xls03版本-->
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.17</version>
</dependency>
<!--xlsx07版本-->
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
</dependency>
<!-- 日期格式化工具-->
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.9</version>
</dependency>
<!-- junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
POI-简单导出
03版本(导出操作)
String PATH = "D:\IDEAworkspace\POI\lr-poi";
@Test
public void testWrite() throws Exception {
//1.创建一个工作簿
Workbook workbook = new HSSFWorkbook();
//2.创建一个工作表
Sheet sheet = workbook.createSheet("狂神观众统计表");
//3.创建第一行
Row row1 = sheet.createRow(0);
//4.创建一个单元格 相当于第一行第一个(1,1)
Cell cell1 = row1.createCell(0);
cell1.setCellValue("今日新增观众");
//相当与第一行第二个(1,2)
Cell cell2 = row1.createCell(1);
cell2.setCellValue(666);
//创建第二行
Row row2 = sheet.createRow(1);
//创建第二行第一列 (2,1)
Cell cell3 = row2.createCell(0);
cell3.setCellValue("统计时间");
//创建第二行第二列 (2,2)
Cell cell4 = row2.createCell(1);
String time = new DateTime().toString("yyyy-MM-dd");
cell4.setCellValue(time);
//生成一张表(IO流) 03版本使用xls结尾
FileOutputStream outputStream = new FileOutputStream(PATH + "狂神统计表03.xls");
workbook.write(outputStream);
//关闭流
outputStream.close();
System.out.println("文件生成完毕!");
}
07版本(导出操作)
@Test
public void testWrite1() throws Exception {
//1.创建一个工作簿
Workbook workbook = new XSSFWorkbook();
//2.创建一个工作表
Sheet sheet = workbook.createSheet("狂神观众统计表");
//3.创建第一行
Row row1 = sheet.createRow(0);
//4.创建一个单元格 相当于第一行第一个(1,1)
Cell cell1 = row1.createCell(0);
cell1.setCellValue("今日新增观众");
//相当与第一行第二个(1,2)
Cell cell2 = row1.createCell(1);
cell2.setCellValue(666);
//创建第二行
Row row2 = sheet.createRow(1);
//创建第二行第一列 (2,1)
Cell cell3 = row2.createCell(0);
cell3.setCellValue("统计时间");
//创建第二行第二列 (2,2)
Cell cell4 = row2.createCell(1);
String time = new DateTime().toString("yyyy-MM-dd");
cell4.setCellValue(time);
//生成一张表(IO流) 03版本使用xls结尾
FileOutputStream outputStream = new FileOutputStream(PATH + "狂神统计表07.xlsx");
workbook.write(outputStream);
//关闭流
outputStream.close();
System.out.println("文件生成完毕!");
}
注意:03版本和07版本只是生成的工作簿对象和后缀不同,效果相同。
POI-批量导出
小文件用HSSF 03版本
缺点:最多只能处理65536行,否则会抛出异常
java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)
优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快
@Test
public void testWrite2() throws Exception {
//记录导出65536行数据多长时间
long begin = System.currentTimeMillis();
//创建一个工作簿
HSSFWorkbook workbook = new HSSFWorkbook();
//创建表
HSSFSheet sheet = workbook.createSheet();
//写入数据 此处如果rowNumber>65536就会报上边出现的错误
for(int rowNumber = 0;rowNumber<65536;rowNumber++ ){
HSSFRow row = sheet.createRow(rowNumber);
for (int cellNum = 0; cellNum < 10; cellNum++) {
HSSFCell cell = row.createCell(cellNum);
cell.setCellValue(cellNum);
}
}
FileOutputStream outputStream = new FileOutputStream(PATH+"测试03.xls");
workbook.write(outputStream);
//关闭资源
outputStream.close();
long end = System.currentTimeMillis();
System.out.println("03版本多少秒"+((double)(end-begin)/1000)); //1.947秒
}
大文件用XSSF 07版本
缺点:写数据时速度非常慢,非常消耗内存,也会发生内存溢出,如100万条数据
优点:可以写较大的数据量,如20万条
@Test
public void testWrite3() throws Exception {
//记录导出65536行数据多长时间
long begin = System.currentTimeMillis();
//创建一个工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
//创建表
XSSFSheet sheet = workbook.createSheet();
//写入数据 此时可以写入的数据可以超过65536
for(int rowNumber = 0;rowNumber<65536;rowNumber++ ){
XSSFRow row = sheet.createRow(rowNumber);
for (int cellNum = 0; cellNum < 10; cellNum++) {
XSSFCell cell = row.createCell(cellNum);
cell.setCellValue(cellNum);
}
}
FileOutputStream outputStream = new FileOutputStream(PATH+"测试07.xlsx");
workbook.write(outputStream);
//关闭资源
outputStream.close();
long end = System.currentTimeMillis();
System.out.println("07版本多少秒"+((double)(end-begin)/1000)); //8.705秒
}
SXSSF 是07版本的加强版 可写入数据更多,速度更快
优点:可以写非常大的数据量,如100万条甚至更多条,写数据速度快,占用更少的内存
注意:
过程中会产生临时文件,需要清理临时文件
默认有100条记录被保存在内存中,如果超过这个数量,则最前面的数据被写入临时文件
如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)
@Test
public void testWrite4() throws Exception {
//记录导出65536行数据多长时间
long begin = System.currentTimeMillis();
//创建一个工作簿
SXSSFWorkbook workbook = new SXSSFWorkbook();
//创建表
SXSSFSheet sheet = workbook.createSheet();
//写入数据
for(int rowNumber = 0;rowNumber<100000;rowNumber++ ){
SXSSFRow row = sheet.createRow(rowNumber);
for (int cellNum = 0; cellNum < 10; cellNum++) {
SXSSFCell cell = row.createCell(cellNum);
cell.setCellValue(cellNum);
}
}
FileOutputStream outputStream = new FileOutputStream(PATH+"测试加速版07.xlsx");
workbook.write(outputStream);
//关闭资源
outputStream.close();
//清除临时文件
workbook.dispose();
long end = System.currentTimeMillis();
System.out.println("07加强版本多少秒"+((double)(end-begin)/1000)); //2.443秒
}
总结:同样是导出65536行数据,03版本用时1.947秒而07版本用时8.705秒,而导出10万条数据07加强版本用时2.443秒。
POI导入
03版本
@Test
public void read1() throws Exception {
//1.获取文件流
FileInputStream inputStream = new FileInputStream(PATH+"狂神统计表03.xls");
//2.创建工作簿
HSSFWorkbook workbook = new HSSFWorkbook(inputStream);
//3.得到表
HSSFSheet sheetAt = workbook.getSheetAt(0);
//4.得到行
HSSFRow row = sheetAt.getRow(0);
//5.得到列
HSSFCell cell = row.getCell(0);
//读取值得时候,一定要注意类型
//cell.getNumericCellValue()获取数字类型
System.out.println(cell.getStringCellValue()); //获取字符串类型
//关闭流
inputStream.close();
}
07版本
@Test
public void read1() throws Exception {
//1.获取文件流
FileInputStream inputStream = new FileInputStream(PATH+"狂神统计表07.xlsx");
//2.创建工作簿
Workbook workbook = new XSSFWorkbook(inputStream);
//3.得到表
Sheet sheetAt = workbook.getSheetAt(0);
//4.得到行
Row row = sheetAt.getRow(0);
//5.得到列
Cell cell = row.getCell(0);
System.out.println(cell.getStringCellValue());
//关闭流
inputStream.close();
}
在做导入的时候报了一个错误:
org.apache.poi.openxml4j.exceptions.OLE2NotOfficeXmlFileException: The supplied data appears to be in the OLE2 Format. You are calling the part of POI that deals with OOXML (Office Open XML) Documents. You need to call a different part of POI to process this data (eg HSSF instead of XSSF)
错误原因:XSSF是用来解析07版本,我却用来解析了03版本,不同版本的excle要使用对应的实现类进行解析。
导入不同的数据类型(也可以叫做通用导入)
Excle文件数据
@Test
public void read2() throws Exception {
//1.获取文件流
FileInputStream inputStream = new FileInputStream("D:\IDEAworkspace\POI\账单.xlsx");
//2.创建一个工作簿
Workbook workbook = new XSSFWorkbook(inputStream);
Sheet sheet = workbook.getSheetAt(0);
//3.获取标题内容
Row rowTitle = sheet.getRow(0);
if (rowTitle!=null){
//rowTitle.getPhysicalNumberOfCells()是获取这一行一共有多少列
int cellCount = rowTitle.getPhysicalNumberOfCells();
for (int cellNumber = 0; cellNumber < cellCount; cellNumber++) {
Cell cell = rowTitle.getCell(cellNumber);
if (cell!=null){
System.out.print(cell.getStringCellValue()+" | "); //手机号 | 消费日期 | 小票号 | 商品编号 | 商品条码 | 商品名称 | 商品单位 | 原价 | 销售价 | 销售数量 | 销售金额 |
}
}
System.out.println();
}
//获取表中的内容
//sheet.getPhysicalNumberOfRows(); 获取这个表中一共有多少行
int rowCount = sheet.getPhysicalNumberOfRows();
//因为第一行为标题,所以rowNuber要从第二行开始
for (int rowNumber = 1; rowNumber < rowCount; rowNumber++) {
Row rowData = sheet.getRow(rowNumber);
if (rowData!=null){
int cellCount = rowTitle.getPhysicalNumberOfCells();
for (int cellNumber = 0; cellNumber < cellCount; cellNumber++) {
System.out.print("["+(rowNumber+1)+"-"+(cellNumber+1)+"]");
Cell cell = rowData.getCell(cellNumber);
//匹配列的数据类型
if (cell!=null){
int cellType = cell.getCellType();
String cellValue = "";
switch (cellType){
case XSSFCell.CELL_TYPE_STRING://字符串类型
System.out.print("【String】");
cellValue = cell.getStringCellValue();
break;
case XSSFCell.CELL_TYPE_BOOLEAN://布尔类型
System.out.print("【Boolen】");
cellValue = String.valueOf(cell.getBooleanCellValue()) ;
break;
case XSSFCell.CELL_TYPE_BLANK://空
System.out.print("【Blank】");//如果是空直接输出
break;
case XSSFCell.CELL_TYPE_NUMERIC://数字(可能是日期或普通数字)
if (HSSFDateUtil.isCellDateFormatted(cell)){ //判断是否是日期类型
System.out.print("【Date】");
Date date = cell.getDateCellValue();
cellValue = new DateTime(date).toString("yyyy-MM-dd");
}else { //如果不是日期类型就是数字类型
//不是日期格式防止日期过长
System.out.print("【转换为字符串输出】");
cell.setCellType(XSSFCell.CELL_TYPE_STRING);
cellValue = cell.toString();
}
break;
case XSSFCell.CELL_TYPE_ERROR://如果是异常类型直接输出
System.out.print("【数据类型错误】");
break;
}
System.out.println(cellValue);
}
}
}
}
inputStream.close();
}
控制台输出:
手机号 | 消费日期 | 小票号 | 商品编号 | 商品条码 | 商品名称 | 商品单位 | 原价 | 销售价 | 销售数量 | 销售金额 |
[2-1]【转换为字符串输出】1312313
[2-2]【Date】2020-01-01
[2-3]【String】0000012312312
[2-4]【String】AA11
[2-5]【String】AA11
[2-6]【String】老坛酸菜
[2-7]【String】坛
[2-8]【转换为字符串输出】100
[2-9]【转换为字符串输出】19999
[2-10]【转换为字符串输出】777
[2-11]【转换为字符串输出】1
[3-1]【转换为字符串输出】123123123
[3-2]【Date】2020-01-02
[3-3]【String】000042345234
[3-4]【String】BB22
[3-5]【String】BB22
[3-6]【String】牛肉面
[3-7]【String】袋
[3-8]【转换为字符串输出】200
[3-9]【转换为字符串输出】18888
[3-10]【转换为字符串输出】999
[3-11]【转换为字符串输出】1