POI讀寫

新手程式設計師暢宇嘯發表於2020-10-26

1.介紹

Apache POI是用Java編寫的免費開源的跨平臺的Java API,Apache POI提供API給Java程式對Microsoft Office格式檔案讀和寫的功能,其中使用最多的就是使用POI操作Excel檔案。

1.1POI的結構

HSSF - 提供讀寫Microsoft Excel XLS格式檔案的功能
XSSF - 提供讀寫Microsoft Excel OOXML XLSX格式檔案的功能
HWPF - 提供讀寫Microsoft Word DOC格式檔案的功能
HSLF - 提供讀寫Microsoft PowerPoint格式檔案的功能
HDGF - 提供讀Microsoft Visio格式檔案的功能
HPBF - 提供讀Microsoft Publisher格式檔案的功能
HSMF - 提供讀Microsoft Outlook格式檔案的功能

2.使用

2.1匯入座標

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.14</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi‐ooxml</artifactId>
    <version>3.14</version>
</dependency>

2.2讀取資料的測試程式碼

 	 /*
    XSSFWorkbook:工作簿
	XSSFSheet:工作表
	Row:行
	Cell:單元格
	*/
	@Test
    public void test1() throws Exception{
        List<Entity> entities = new ArrayList<>();
        //載入指定檔案,建立一個Excel物件(工作簿)
        Workbook excel = new XSSFWorkbook(new FileInputStream(new File("d:\\poi.xlsx")));
        //讀取Excel檔案中第一個Sheet標籤頁
        Sheet sheet = excel.getSheetAt(0);
        //遍歷Sheet標籤頁,獲得每一行資料
        int row = 0;
        int cell = 0;
        for (Row row : sheet) {
            cell = 0;
            if(row == 0)
                continue;
            Entity entity = new Entity();
            //遍歷行,獲得每個單元格物件
            for (Cell cell : row) {
                
                if(cell == 0){
                    entity.setName("cell.getStringCellValue()");
                    System.out.println(cell.getStringCellValue());
                }else if(cell == 1){
                    entity.setAddress("cell.getStringCellValue()");
                }else if (cell == 2){
                    //設定年齡
                }
                
                cell++;
            }
            entities.add(entity);
            row++;
        }
        //關閉資源
        excel.close();
    }
  

1.XSSFWorkbook用來讀寫XLSX格式的檔案
2.構造XSSFWorkbook時需要傳入inputstream物件
3.getSheetAt(x)可以獲取第x+1個Sheet標籤頁,返回XSSFSheet物件
4. XSSFSheet 實現了 Sheet extends Iterable 介面,所以可以直接進行遍歷,獲取每一行的資料
5. 遍歷Row可以獲取每個單元格的資料Cell

 	@Test
    public void test2() throws Exception{
        //載入指定檔案,建立一個Excel物件(工作簿)
        XSSFWorkbook excel = new XSSFWorkbook(new FileInputStream(new File("d:\\poi.xlsx")));
        //讀取Excel檔案中第一個Sheet標籤頁
        XSSFSheet sheet = excel.getSheetAt(0);
        //獲得當前工作表中最後一個行號,需要注意:行號從0開始
        int lastRowNum = sheet.getLastRowNum();  
        System.out.println("lastRowNum = " + lastRowNum); 
        for(int i=0;i<=lastRowNum;i++){
            XSSFRow row = sheet.getRow(i);//根據行號獲取每一行
            //獲得當前行最後一個單元格索引
            short lastCellNum = row.getLastCellNum();
            System.out.println("lastCellNum = " + lastCellNum);
            for(int j=1;j<=lastCellNum;j++){
                XSSFCell cell = row.getCell(j);//根據單元格索引獲得單元格物件
                System.out.println(cell.getStringCellValue());
            }
        }
        //關閉資源
        excel.close();
    }

getLastRowNum 返回的最後的行號,比如兩行,返回的是1
getLastCellNum 返回的是單元格數量,比如兩列,返回的是2

2.3寫入資料的測試程式碼

//使用POI向Excel檔案寫入資料,並且通過輸出流將建立的Excel檔案儲存到本地磁碟
    @Test
    public void test3() throws Exception{
        //在記憶體中建立一個Excel檔案(工作簿)
        XSSFWorkbook excel = new XSSFWorkbook();
        //建立一個工作表物件
        XSSFSheet sheet = excel.createSheet("傳智播客");
        //在工作表中建立行物件
        XSSFRow title = sheet.createRow(0);
        //在行中建立單元格物件
        title.createCell(0).setCellValue("姓名");
        title.createCell(1).setCellValue("地址");
        title.createCell(2).setCellValue("年齡");

        XSSFRow dataRow = sheet.createRow(1);
        dataRow.createCell(0).setCellValue("小明");
        dataRow.createCell(1).setCellValue("北京");
        dataRow.createCell(2).setCellValue(20);
        
        //建立一個輸出流,通過輸出流將記憶體中的Excel檔案寫到磁碟
        FileOutputStream out = new FileOutputStream(new File("d:\\xieru.xlsx"));
        excel.write(out);
        out.flush();
        excel.close();
    }

1.XSSFWorkbook可以直接new建立
2.使用XSSFWorkbook.createSheet建立一個工作表
3.XSSFSheet.createRow建立行,返回XSSFRow物件
4.XSSFRow.createCell(int)建立單元格,直接對單元格setCellValue寫入資料
5.建立一個outputstream寫入,通過XSSFWorkbook.write寫入流

3.POI的工具類

public class POIUtils {
    private final static String xls = "xls";
    private final static String xlsx = "xlsx";
    //格式化時間字串
    private final static String DATE_FORMAT = "yyyy/MM/dd";
    /**
     * 讀入excel檔案,解析後返回List(ExcelModel)
     * @param file
     * @throws IOException
     */
    public static List<String[]> readExcel(MultipartFile file) throws IOException {
        //檢查檔案
        checkFile(file);
        //獲得Workbook工作薄物件
        Workbook workbook = getWorkBook(file);
        //建立返回物件,把每行中的值作為一個陣列,所有行作為一個集合返回
        List<String[]> list = new ArrayList<String[]>();
        if(workbook != null){
            for(int sheetNum = 0;sheetNum < workbook.getNumberOfSheets();sheetNum++){
                //獲得當前sheet工作表
                Sheet sheet = workbook.getSheetAt(sheetNum);
                if(sheet == null){
                    continue;
                }
                //獲得當前sheet的開始行
                int firstRowNum  = sheet.getFirstRowNum();
                //獲得當前sheet的結束行
                int lastRowNum = sheet.getLastRowNum();
                //迴圈除了第一行的所有行
                for(int rowNum = firstRowNum+1;rowNum <= lastRowNum;rowNum++){
                    //獲得當前行
                    Row row = sheet.getRow(rowNum);
                    if(row == null){
                        continue;
                    }
                    //獲得當前行的開始列
                    int firstCellNum = row.getFirstCellNum();
                    //獲得當前行的列數
                    int lastCellNum = row.getPhysicalNumberOfCells();
                    String[] cells = new String[row.getPhysicalNumberOfCells()];
                    //迴圈當前行
                    for(int cellNum = firstCellNum; cellNum < lastCellNum;cellNum++){
                        Cell cell = row.getCell(cellNum);
                        cells[cellNum] = getCellValue(cell);
                    }
                    list.add(cells);
                }
            }
            workbook.close();
        }
        return list;
    }

    //校驗檔案是否合法
    public static void checkFile(MultipartFile file) throws IOException{
        //判斷檔案是否存在
        if(null == file){
            throw new FileNotFoundException("檔案不存在!");
        }
        //獲得檔名
        String fileName = file.getOriginalFilename();
        //判斷檔案是否是excel檔案
        if(!fileName.endsWith(xls) && !fileName.endsWith(xlsx)){
            throw new IOException(fileName + "不是excel檔案");
        }
    }
    public static Workbook getWorkBook(MultipartFile file) {
        //獲得檔名
        String fileName = file.getOriginalFilename();
        //建立Workbook工作薄物件,表示整個excel
        Workbook workbook = null;
        try {
            //獲取excel檔案的io流
            InputStream is = file.getInputStream();
            //根據檔案字尾名不同(xls和xlsx)獲得不同的Workbook實現類物件
            if(fileName.endsWith(xls)){
                //2003
                workbook = new HSSFWorkbook(is);
            }else if(fileName.endsWith(xlsx)){
                //2007
                workbook = new XSSFWorkbook(is);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return workbook;
    }
    
    public static String getCellValue(Cell cell){
        String cellValue = "";
        if(cell == null){
            return cellValue;
        }
        //如果當前單元格內容為日期型別,需要特殊處理
        String dataFormatString = cell.getCellStyle().getDataFormatString();
        if(dataFormatString.equals("m/d/yy")){
            cellValue = new SimpleDateFormat(DATE_FORMAT).format(cell.getDateCellValue());
            return cellValue;
        }
        //把數字當成String來讀,避免出現1讀成1.0的情況
        if(cell.getCellType() == Cell.CELL_TYPE_NUMERIC){
            cell.setCellType(Cell.CELL_TYPE_STRING);
        }
        //判斷資料的型別
        switch (cell.getCellType()){
            case Cell.CELL_TYPE_NUMERIC: //數字
                cellValue = String.valueOf(cell.getNumericCellValue());
                break;
            case Cell.CELL_TYPE_STRING: //字串
                cellValue = String.valueOf(cell.getStringCellValue());
                break;
            case Cell.CELL_TYPE_BOOLEAN: //Boolean
                cellValue = String.valueOf(cell.getBooleanCellValue());
                break;
            case Cell.CELL_TYPE_FORMULA: //公式
                cellValue = String.valueOf(cell.getCellFormula());
                break;
            case Cell.CELL_TYPE_BLANK: //空值
                cellValue = "";
                break;
            case Cell.CELL_TYPE_ERROR: //故障
                cellValue = "非法字元";
                break;
            default:
                cellValue = "未知型別";
                break;
        }
        return cellValue;
    }
}

4.Easypoi

4.1介紹

Easypoi的目標不是替代poi,而是讓一個不懂匯入匯出的快速使用poi完成Excel和word的各種操作,而不是看很多api才可以完成這樣工作

4.2使用

參考資料: https://opensource.afterturn.cn/doc/easypoi.html.

相關文章