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