POI

POI介绍

官网图片

Apache POI 简介是用 Java 编写的免费开源的跨平台的 Java API,Apache POI 提供 API 给 Java 程式对 Microsoft Office(Excel、WORD、PowerPoint、Visio 等)格式档案读和写的功能。POI 为“Poor Obfuscation Implementation”的首字母缩写。

官网以及参考的文档地址:

POI常用的类和接口

Apache POI 包含类和方法,来将 MS Office 所有 OLE 2 文档复合。此 API 组件的列表如下:

  • HSSF 提供读写 Microsoft Excel XLS 格式档案的功能
  • XSSF 提供读写 Microsoft Excel OOXML XLSX 格式档案的功能
  • HWPF 提供读写 Microsoft Word DOC 格式档案的功能
  • HSLF 提供读写 Microsoft PowerPoint 格式档案的功能
  • HDGF 提供读 Microsoft Visio 格式档案的功能
  • ……

POI 很强大,不单单可以操作 Excle 还可以操作 PPT、Word……但是我们在实际开发中百分之九十都是操作 Excel 的,使用 POI 操作 Excel 的话无非两种情况一张使用操作 2003 版本的 Excel,一种是操作 2007 版本的 Excel。

POI常用类和方法

使用 POI 操作 Excel 的话无非两种情况一种是操作 2003+ 版本的 Excel,使用的接口对象是(HSSF)开头的对象,另一种是操作 2007+ 版本的 Excel,接口对象是(XSSF)开头。

Excel 中的工作簿、工作表、行、单元格中的关系:

  • 一个 Excel 文件对应于一个 workbook(HSSFWorkbook、XSSFWorkbook)
  • 一个 workbook 可以有多个 sheet(HSSFSheet、XSSFSheet)组成
  • 一个 sheet 是由多个 row(HSSFRow、XSSFRow)组成
  • 一个 row 是由多个 cell(HSSFCell、XSSFCell)组成

2003 版本常用类和方法

HSSFWorkbook:工作簿

工作簿,代表一个 Excel 的整个文档。

1
2
3
4
5
6
7
8
9
10
11
HSSFWorkbook(); // 创建一个新的工作簿
HSSFWorkbook(InputStream inputStream); // 创建一个关联输入流的工作簿,可以将一个 excel 文件封装成工作簿
HSSFSheet createSheet(String sheetname); //创建一个新的 Sheet
HSSFSheet getSheet(String sheetName); //通过名称获取 Sheet
HSSFSheet getSheetAt(int index); // 通过索引获取 Sheet,索引从 0 开始
HSSFCellStyle createCellStyle()// 创建单元格样式
int getNumberOfSheets(); //获取 sheet 的个数
setActiveSheet(int index); //设置默认选中的工作表
write();
write(File newFile);
write(OutputStream stream);

HSSFSheet:工作表

1
2
3
4
5
6
7
HSSFRow createRow(int rownum); //创建新行,需要指定行号,行号从 0 开始
HSSFRow getRow(int index); //根据索引获取指定的行
int addMergedRegion(CellRangeAddress region); //合并单元格
CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol); //单元格范围, 用于合并单元格,需要指定要合并的首行、最后一行、首列、最后一列。
autoSizeColumn(int column); //自动调整列的宽度来适应内容
getLastRowNum(); //获取最后的行的索引,没有行或者只有一行的时候返回 0
setColumnWidth(int columnIndex, int width); //设置某一列的宽度,width=字符个数 * 256,例如 20 个字符的宽度就是 20 * 256

HSSFRow:行

1
2
3
4
5
6
HSSFCell createCell(int column); //创建新的单元格
HSSFCell setCell(shot index);
HSSFCell getCell(shot index);
setRowStyle(HSSFCellStyle style); //设置行样式
short getLastCellNum(); //获取最后的单元格号,如果单元格有第一个开始算,lastCellNum 就是列的个数
setHeightInPoints(float height); //设置行的高度

HSSFCell:单元格

1
2
3
4
5
6
setCellValue(String value); //设置单元格的值
setCellType(); //设置单元格类型,如字符串、数字、布尔等
setCellStyle(); //设置单元格样式
String getStringCellValue(); //获取单元格中的字符串值
setCellStyle(HSSFCellStyle style); //设置单元格样式,例如字体、加粗、格式化
setCellFormula(String formula); //设置计算公式,计算的结果作为单元格的值,也提供了异常常用的函数,如求和"sum(A1,C1)"、日期函数、字符串相关函数、CountIf 和 SumIf 函数、随机数函数等

2007 版本常用类和方法

XSSFWorkbook:工作簿

1
2
3
4
5
6
7
8
9
10
11
XSSF Workbook(); // 创建一个新的工作簿
XSSF Workbook(InputStream inputStream); // 创建一个关联输入流的工作簿,可以将一个 excel 文件封装成工作簿
XSSF Sheet createSheet(String sheetname); //创建一个新的 Sheet
XSSF Sheet getSheet(String sheetName); //通过名称获取 Sheet
XSSF Sheet getSheetAt(int index); // 通过索引获取 Sheet,索引从 0 开始
XSSF CellStyle createCellStyle()//创建单元格样式
int getNumberOfSheets(); //获取 sheet 的个数
setActiveSheet(int index); //设置默认选中的工作表
write();
write(File newFile);
write(OutputStream stream);

XSSFSheet:工作表

1
2
3
4
5
6
7
XSSF Row createRow(int rownum); //创建新行,需要指定行号,行号从 0 开始
XSSF Row getRow(int index); //根据索引获取指定的行
int addMergedRegion(CellRangeAddress region); //合并单元格
CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol); //单元格范围, 用于合并单元格,需要指定要合并的首行、最后一行、首列、最后一列。
autoSizeColumn(int column); //自动调整列的宽度来适应内容
getLastRowNum(); //获取最后的行的索引,没有行或者只有一行的时候返回 0
setColumnWidth(int columnIndex, int width); //设置某一列的宽度,width=字符个数 * 256,例如 20 个字符的宽度就是 20 * 256

XSSFRow :行

1
2
3
4
5
6
XSSF Cell createCell(int column); //创建新的单元格
XSSF Cell setCell(shot index);
XSSF Cell getCell(shot index);
setRowStyle(XSSF CellStyle style); //设置行样式
short getLastCellNum(); //获取最后的单元格号,如果单元格有第一个开始算,lastCellNum 就是列的个数
setHeightInPoints(float height); //设置行的高度

XSSFCell:单元格

1
2
3
4
5
6
setCellValue(String value); //设置单元格的值
setCellType(); //设置单元格类型,如字符串、数字、布尔等
setCellStyle(); //设置单元格样式
String getStringCellValue(); //获取单元格中的字符串值
setCellStyle(XSSF CellStyle style); //设置单元格样式,例如字体、加粗、格式化
setCellFormula(String formula); //设置计算公式,计算的结果作为单元格的值,也提供了异常常用的函数,如求和"sum(A1,C1)"、日期函数、字符串相关函数、CountIf 和 SumIf 函数、XSSF

POI 中 Excel 组成和步骤

组成

Excel 的文件的组成形式:

  • 一个 Excel 文件对应于一个 workbook(XSSFWorkbook,工作薄)
  • 一个 workbook 可以有多个 sheet(XSSFSheet,工作表)组成
  • 一个 sheet 是由多个 row(Row,工作行)组成
  • 一个 row 是由多个 cell(Cell,工作单元格)组成

操作步骤

基于以上几条,如果想对 Excel 文件进行读写的话就要需要执行下面这几个步骤:

  1. 用 Workbook 打开或者创建一个 Excel 文件的对象

  2. 用上一步的 Excel 对象创建或者获取到一个 Sheet 对象

  3. 用 Sheet 对象创建或获取一个 Row 对象

  4. 用 Row 对象创建或获取一个 Cell 对象

  5. 对 Cell 对象读写

2003 版本和 2007 版本

操作 Excel 文件区分版本如下。

2003+版本(包含 2003)文件的扩展名为 .xls 需要用 HSSFWorkbook 类操作:

在这里插入图片描述

2007+ 版本(包含 2007)文件的扩展名为 .xlsx 需要用 XSSFWorkbook 类操作:

在这里插入图片描述

img

需要注意:2003 版本和 2007 版本存在兼容性的问题!2003 最多只有 65536 行!2007 最多是 1048576。

2007 版及以上的其实也是有限制的,最大是 1048576。

实战案例:POI操作案例

注意:poi的引入,从版本 4.0.1 开始,POI 需要 Java 8 或更高版本,目前官网,稳定说的 4.1.2 是最新稳定版本

创建maven工具,并引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
<!-- xls(2003)版本 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- xlsx(2007)版本及以上 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>

写Excel

img

从上面的接口对于Excel抽取出几个核心对象:

  • 工作薄(Workbook)
  • 工作表(Sheet)
  • 行(行是由单元格组成)(Row)
  • 单元格(Cell)

关系简单来说就是:

  • 一个工作薄中可以有很多个工作表
  • 一个工作中表可以有很多个工作行
  • 一个工作行中可以有很多个工作单元格

03 版本的 POI 操作写 Excel(案例)Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.poi.demo;

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 java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
* 03 版本的 POI 操作写 Excel(案例)Demo
*
* @author jingLv
* @date 2020/11/05
*/
public class WriteExcelFor03 {

public static void main(String[] args) {
// 定义输出路径
String path = "/Users/apple/JavaProject/operation-excel/";

// 1.创建工作簿
Workbook workbook = new HSSFWorkbook();
// 2.创建工作表
Sheet sheet = workbook.createSheet("excel write test");
// 3.创建工作行
// 参数0 就代表Excel中的第一行
Row row1 = sheet.createRow(0);
// 4.创建工作单元格
// 参数0 就代表Excel中的第一个单元格
Cell cell1 = row1.createCell(0);
// 填充Excel中的第一行的第一个单元格(0,0)
cell1.setCellValue("name:Jack");

// 填充Excel中的第一行的第二个单元格(0,1)
Cell cell2 = row1.createCell(1);
cell2.setCellValue("age:20");

// 创建第二行 参数1 就代表Excel中的第二行
Row row2 = sheet.createRow(1);
// 第二行的第一个单元格,参数0 就代表Excel中的第一个单元格
Cell cell21 = row2.createCell(0);
// 填充Excel中的第一行的第一个单元格(0,0)
cell21.setCellValue("name:May");

// 填充Excel中的第一行的第二个单元格(0,1)
Cell cell22 = row2.createCell(1);
cell22.setCellValue("age:16");

//上面已经把所需要的数据已经构建成功,这时候需要生成一张 Excel 数据表。其实生成表的话本质上是 IO 操作,IO 操作就需要流,所以说我们要构建一个流。输出到那个位置上
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(path + "excel2003.xls");
try {
((HSSFWorkbook) workbook).write(fileOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
//关闭流
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("-----------POI 写入 Excel 成功");
}
}

执行结果:

image-20201106105639688

打开excel:

image-20201106105710496

07 版本的 POI 操作写 Excel(案例)Demo

查看Workbook是一个接口,Java也是面向接口编程的,对象变了接口不嫌,所以说只需把 03 版本的接口实现换为 07 版本的接口实现即可。(HSSFWorkbook 换为 XSSFWorkbook)和把 xxx.xls 结尾的后缀改为 xxx.xlsx。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.poi.demo;

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 java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
* 07 版本的 POI 操作写 Excel(案例)Demo
*
* @author jingLv
* @date 2020/11/05
*/
public class WriteExcelFor07 {

public static void main(String[] args) {
// 定义输出路径
String path = "/Users/apple/JavaProject/operation-excel/";

// 1.创建工作簿
Workbook workbook = new XSSFWorkbook();
// 2.创建工作表
Sheet sheet = workbook.createSheet("excel write test");
// 3.创建工作行
// 参数0 就代表Excel中的第一行
Row row1 = sheet.createRow(0);
// 4.创建工作单元格
// 参数0 就代表Excel中的第一个单元格
Cell cell1 = row1.createCell(0);
// 填充Excel中的第一行的第一个单元格(0,0)
cell1.setCellValue("name:Jack");

// 填充Excel中的第一行的第二个单元格(0,1)
Cell cell2 = row1.createCell(1);
cell2.setCellValue("age:20");

// 创建第二行 参数1 就代表Excel中的第二行
Row row2 = sheet.createRow(1);
// 第二行的第一个单元格,参数0 就代表Excel中的第一个单元格
Cell cell21 = row2.createCell(0);
// 填充Excel中的第一行的第一个单元格(0,0)
cell21.setCellValue("name:May");

// 填充Excel中的第一行的第二个单元格(0,1)
Cell cell22 = row2.createCell(1);
cell22.setCellValue("age:16");

//上面已经把所需要的数据已经构建成功,这时候需要生成一张 Excel 数据表。其实生成表的话本质上是 IO 操作,IO 操作就需要流,所以说我们要构建一个流。输出到那个位置上
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(path + "excel2007.xlsx");
try {
workbook.write(fileOutputStream);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
//关闭流
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
System.out.println("-----------POI 写入 Excel 成功");
}
}

执行结果:

image-20201106120314565

读Excel

03 版本的 POI 操作读 Excel(案例)Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.poi.demo;

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 java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
* 03 版本的 POI 操作读 Excel(案例)Demo
*
* @author jingLv
* @date 2020/11/05
*/
public class ReadExcelFor03 {

public static void main(String[] args) {
// 定义文件路径
String path = "/Users/apple/JavaProject/operation-excel/";

// 1. 获取文件的流
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(path + "excel2003.xls");
// 2. 创建工作簿,把获取的流传入
Workbook workbook = new HSSFWorkbook(fileInputStream);
// 3. 创建表
Sheet sheet = workbook.getSheetAt(0);
// 4. 创建行
Row row1 = sheet.getRow(0);
// 5. 创建单元格
Cell cell1 = row1.getCell(0);
// 6. 读取Excel内容
System.out.println(cell1.getStringCellValue());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 7. 关闭流
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

07 版本的 POI 操作读 Excel(案例)Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.poi.demo;

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 java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
* 07 版本的 POI 操作读 Excel(案例)Demo
*
* @author jingLv
* @date 2020/11/05
*/
public class ReadExcelFor07 {

public static void main(String[] args) {
// 定义文件路径
String path = "/Users/apple/JavaProject/operation-excel/";

// 1. 获取文件的流
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(path + "excel2007.xlsx");
// 2. 创建工作簿,把获取的流传入
Workbook workbook = new XSSFWorkbook(fileInputStream);
// 3. 创建表
Sheet sheet = workbook.getSheetAt(0);
// 4. 创建行
Row row1 = sheet.getRow(0);
// 5. 创建单元格
Cell cell1 = row1.getCell(0);
// 6. 读取Excel内容
System.out.println(cell1.getStringCellValue());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 7. 关闭流
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

读取 Excel 时不同的数据类型怎样处理

Excel 中是可以给列设置数据类型的,那不同的类型数据,我们应该怎样获取?

注意,在读取单元格的值时需要先判断数据类型,不然获取数据会报数据类型错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package com.poi.demo;

import cn.hutool.core.date.DateTime;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Date;

/**
* @author jingLv
* @date 2020/11/06
*/
public class ExcelCellType {

public static void main(String[] args) {
// 定义文件路径
String path = "/Users/apple/JavaProject/operation-excel/";

// 获取文件流
FileInputStream fileInputStream = null;

try {
fileInputStream = new FileInputStream(path + "type.xlsx");
// 1. 创建工作簿,将文件流导入
Workbook workbook = new XSSFWorkbook(fileInputStream);
// 2. 获取工作表
Sheet sheet = workbook.getSheetAt(0);
// Excel中第一行都是标题,先读取标题
Row rowTitle = sheet.getRow(0);
// 获取Excel的标题
if (null != rowTitle) {
// 如果标题不为空的话
int cellCount = rowTitle.getPhysicalNumberOfCells();
for (int cellNumber = 0; cellNumber < cellCount; cellNumber++) {
Cell cell = rowTitle.getCell(cellNumber);
if (null != cell) {
CellType cellType = cell.getCellType();
String cellValue = cell.getStringCellValue();
System.out.print(cellValue + "||");
}
}
// 空行
System.out.println();
}
// 获取表中的内容
int rowCount = sheet.getPhysicalNumberOfRows();
// 下标从1开始,排除行标题
for (int rowNumber = 1; rowNumber < rowCount; rowNumber++) {
Row rowData = sheet.getRow(rowNumber);
if (null != rowData) {
// 读取列
int cellCount = rowTitle.getPhysicalNumberOfCells();
for (int cellNumber = 0; cellNumber < cellCount; cellNumber++) {
Cell cell = rowData.getCell(cellNumber);
if (null != cell) {
CellType cellType = cell.getCellType();
String cellValue = "";
switch (cellType) {
// 字符串
case STRING:
cellValue = cell.getStringCellValue();
break;
// 布尔
case BOOLEAN:
cellValue = String.valueOf(cell.getStringCellValue());
break;
// 数字(日期、普通数字)
case NUMERIC:
// 日期
if (DateUtil.isCellDateFormatted(cell)) {
System.out.print("【日期】");
Date date = cell.getDateCellValue();
cellValue = new DateTime(date).toString("yyyy-MM-dd");
} else {
// 不是日期格式,防止数字过长!
System.out.print("【转换为字符串输出】");
cell.setCellType(CellType.STRING);
cellValue = cell.toString();
}
break;
// 空
case BLANK:
System.out.print("【BLANK】");
break;
case ERROR:
System.out.print("【数据类型错误】");
break;
}
System.out.println(cellValue);
}
}
}

}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}