POI
1. 简介
Apache POl是基于Office Open XML标准(O0XML)和Microsot的OLE2复合文档格式(OLE2)处理各种文件格式的开源项目。简而言之,您可以使用java读写MS Excel文件,可以使用java读写MS Word和MS PowerPoint文件。
2. 模块
- HSSF:提供读写Microsoft Excel XLS格式 (Microsoft Excel 97(-2003))档案的功能。
- XSSF:提供读写Microsoft Excel OOXML XLSX格式(Microsoft Excel XML(2007+))档案的功能。
- SXSSF:提供低内存占用量读写 Microsoft Excel OOXML XLSX 格式档案的功能。
- HWPF:提供读写Microsoft Word DOC97格式 (Microsoft Word 97 (-2003)档案的功能。
- XWPF:提供读写Microsoft Word DOC2003格式(WordprocessingML(2007+))档案的功能。
- HSLF/XSLF:提供读写Microsoft PowerPoint格式档案的功能。
- HDGF/XDGF:提供读Microsoft Visio格式档案的功能。
- HPBF:提供读Microsoft Publisher格式档案的功能。
- HSMF:提供读Microsoft Outlook格式档案的功能。
3. POI存在的问题
Java解析、生成Excel比较有名的框架有PO1、JXL。但是它们都存在一个严重的问题,就是非常损耗内存,也就是数据量比较大的时候有可能会出现OOM的问题,但是POI有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但是依旧没有完全的解决内存消耗过大的问题。
4. Excel03与07版本区别
POI可以支持不同版本Excel表格的操作,对应的版本有03和07+版本,那么这两种表格的区别如下: 03版本:最多行数支持:65536,并且后缀为.xls。
07+版本:理论上没有限制,但实际支持行数为:1048576,并且后缀为.xlsx
5. POI操作Excel
添加pom依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.4.0</version>
</dependency>
5.1 读写03版本Excel
- 写文件
String filePath = "D:\\03excelData.xls";
// 创建工作簿
HSSFWorkbook workbook = new HSSFWorkbook();
// 创建工作表
HSSFSheet sheet = workbook.createSheet("测试");
// 创建行
HSSFRow headRow = sheet.createRow(0);
// 创建单元格
headRow.createCell(0, CellType.STRING).setCellValue("商品ID");
headRow.createCell(1, CellType.STRING).setCellValue("商品名称");
HSSFRow dataRow = sheet.createRow(1);
dataRow.createCell(0, CellType.NUMERIC).setCellValue(1.123);
dataRow.createCell(1, CellType.STRING).setCellValue("test01");
try(FileOutputStream outputStream = new FileOutputStream(filePath)){
workbook.write(outputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
- 读文件
public static void main(String[] args) throws IOException {
String filePath = "D:\\03excelData.xls";
FileInputStream inputStream = new FileInputStream(filePath);
HSSFWorkbook workbook = new HSSFWorkbook(inputStream);
// 通过索引下标或者sheet的名称
HSSFSheet sheet = workbook.getSheetAt(0);
int lastRowNum = sheet.getLastRowNum();
for (int i = 0; i <= lastRowNum; i++) {
HSSFRow row = sheet.getRow(i);
int cellCount = row.getPhysicalNumberOfCells();
for (int j = 0; j < cellCount; j++) {
HSSFCell cell = row.getCell(j);
CellType cellType = cell.getCellType();
if(cellType.equals(CellType.STRING)){
System.out.println(cell.getStringCellValue());
} else if (cellType.equals(CellType.NUMERIC)) {
System.out.println(cell.getNumericCellValue());
}else{
System.out.println(cell.getStringCellValue());
}
}
}
}
5.2 读写07版本Excel
- 写文件
public static void main(String[] args) throws IOException {
String filePath = "D:\\07excelData.xlsx";
// 创建工作簿
XSSFWorkbook workbook = new XSSFWorkbook();
// 创建工作表
XSSFSheet sheet = workbook.createSheet("测试");
// 创建行
XSSFRow headRow = sheet.createRow(0);
// 创建单元格
headRow.createCell(0, CellType.STRING).setCellValue("商品ID");
headRow.createCell(1, CellType.STRING).setCellValue("商品名称");
XSSFRow dataRow = sheet.createRow(1);
dataRow.createCell(0, CellType.NUMERIC).setCellValue(1.123);
dataRow.createCell(1, CellType.STRING).setCellValue("test01");
try(FileOutputStream outputStream = new FileOutputStream(filePath)){
workbook.write(outputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
- 读文件
public static void main(String[] args) throws IOException {
String filePath = "D:\\07excelData.xlsx";
FileInputStream inputStream = new FileInputStream(filePath);
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
// 通过索引下标或者sheet的名称
XSSFSheet sheet = workbook.getSheetAt(0);
int rowNum = sheet.getPhysicalNumberOfRows();
for (int i = 0; i <= rowNum; i++) {
XSSFRow row = sheet.getRow(i);
int cellCount = row.getPhysicalNumberOfCells();
for (int j = 0; j < cellCount; j++) {
XSSFCell cell = row.getCell(j);
CellType cellType = cell.getCellType();
/**
* CellType中定义了不同的枚举类型,来作为表格数据的接收类型
* NONE 未知类型
* NUMERIC 数值类型(整数、小数、日期)
* STRING 字符串
* FORMULA 公式
* BLANK 空字符串(没有值),但是有单元格样式
* BOOLEAN 布尔值
* ERROR 错误单元格
*/
if(cellType.equals(CellType.STRING)){
System.out.println(cell.getStringCellValue());
} else if (cellType.equals(CellType.NUMERIC)) {
System.out.println(cell.getNumericCellValue());
}else{
System.out.println(cell.getStringCellValue());
}
}
}
}
6. 批量数据写入
6.1 03版本HSSF
最多支持65536行数据的写入,超出会抛出异常。
操作方式为,先将所有的数据放入到缓存中,最后一次性写入磁盘,写入速度快。
6.2 07+版本XSSF
数据的写入速度会比较慢,因为XSSF是获取全部行的数据,因此会消耗大量内存,数据量庞大时就有可能会发现内存溢出。可以写入较大的数据,比如10W+条数据。
最后我们还明显的发现,XSSF的速度明显要慢,大于9秒才完成数据的写入,但是它可以写入更多的数据
6.3 使用SXSSF
为了解决大数据量写入的问题,所以官方提供了SXSSF来解决大文件写入问题,所以它可以写入非常大量的数据,比如上百万条数据,并且写入速度更快,占用内存更少。
SXSSF在兼容XSSF的同时,能够应对大数据量和内存空间有限的情况。SXSSF每次获取的行数是在一个数值范围内,这个范围被称为“滑动窗口",在这个窗口内的数据均存在于内存中,超出这个窗口大小时,数据会被写入磁盘,由此控制内存使用,相比较而言,XSSF则每次都是获取全部行。
窗口默认大小为100(可以自定义通过构造函数传入),如果数据为101行,那么此时超出了窗口限制,索引值最小的行会被“刷入”磁盘,需要注意,SXSSF会自动分配临时文件,这些临时文件需要我们手动清除,清除的方式是使用dispose()方法中。
public static void main(String[] args) throws IOException {
List<UserInfo> list = new ArrayList<>();
// 最大1048576
for (int i = 0; i < 1000000; i++) {
UserInfo userInfo = new UserInfo();
userInfo.setName("jack"+(i+1));
userInfo.setAge(i+1);
list.add(userInfo);
}
String filePath = "D:\\batchExcelData.xlsx";
// 创建工作簿
SXSSFWorkbook workbook = new SXSSFWorkbook(2000);
// 创建工作表
SXSSFSheet sheet = workbook.createSheet("测试");
// 创建行
SXSSFRow headRow = sheet.createRow(0);
// 创建单元格
headRow.createCell(0, CellType.STRING).setCellValue("用户名");
headRow.createCell(1, CellType.STRING).setCellValue("年龄");
for (int i = 0; i < list.size(); i++) {
UserInfo userInfo = list.get(i);
SXSSFRow dataRow = sheet.createRow(i+1);
dataRow.createCell(0, CellType.STRING).setCellValue(userInfo.getName());
dataRow.createCell(1, CellType.NUMERIC).setCellValue(userInfo.getAge());
}
try(FileOutputStream outputStream = new FileOutputStream(filePath)){
workbook.write(outputStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
7. 使用公式
一般比较少用,因为公式的计算也可以放到我们的java代码中去做,公式的结果通过POI读取到:
public static void main(String[] args) throws IOException {
String filePath = "D:\\07excelData.xlsx";
FileInputStream inputStream = new FileInputStream(filePath);
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
// 通过索引下标或者sheet的名称
XSSFSheet sheet = workbook.getSheetAt(1);
XSSFRow row1 = sheet.getRow(2);
XSSFCell cell = row1.getCell(0);
XSSFFormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator(workbook);
if(cell.getCellType().equals(CellType.FORMULA)){
// 获取计算公式
String cellFormula = cell.getCellFormula();
System.out.println(cellFormula);
// 计算公式进行计算
CellValue value = formulaEvaluator.evaluate(cell);
System.out.println(value.formatAsString());
}
}
运行结果: