1. 程式人生 > 實用技巧 >POI匯入匯出基本操作

POI匯入匯出基本操作

POI

什麼是POI

POI是Apache軟體基金會用Java編寫的免費開源的跨平臺的 Java API,Apache POI提供API給Java程式對Microsoft Office格式檔案讀和寫的功能。POI為“Poor Obfuscation Implementation”的首字母縮寫,意為“簡潔版的模糊實現”。
所以POI的主要功能是可以用Java操作Microsoft Office的相關檔案,但是一般我們都是用來操作Excel相關檔案。

POI使用場景

  1. 將資料庫資訊匯出為Excle表格(俗稱:匯出資料)
  2. 將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