POI匯入匯出基本操作
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