能夠讀取Excel是JasperReports沒有的功能,今天就使用Apache POI來讀取Apache POI-寫入(匯出)Excel這篇所匯出的學生成績資料表。
create()方法,能夠傳入File、Inputstream等等參數,就能輕鬆取得文件的Workbook。 不論要讀取的Excel是.xls還是.xlsx,不用寫判斷邏輯就可以拿到Workbook,充分顯示物件導向中多型的優點。 
下圖是學生成績資料表的資料:
我想將讀取到的欄位資料放到StudentCourseScoreReportModel這個物件中
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentCourseScoreReportModel {
    private String studentNumber;
    private String fullName;
    private String grade;
    private String courseDesc;
    private String departmentDesc;
    private Integer score;
    private String testDate;
}
步驟如下,第幾步可以對應註解來看:
getSheetAt()取第一個,如果要寫得更通用一些可以用getNumberOfSheets()取得sheet的數量再以for loop遍歷。getLastRowNum()取得有內容的最後一列的index,只處理有資料的部分就好。getRow取得Row,並且準備好StudentCourseScoreReportModel,在下面的步驟要放入Row中讀取到的資料。getCell()取得Cell。 
public void readExcel(String filePath) {
    // 1. 欄位名稱
    String[] columnNames = {"學號", "科系", "年級", "姓名", "課程", "成績", "考試日期"};
    List<StudentCourseScoreReportModel> modelList = new ArrayList<>();
    try {
        InputStream inputStream = Files.newInputStream(Paths.get(filePath));
        // 2. 取得Workbook
        Workbook workbook = WorkbookFactory.create(inputStream);
        // 3. 取得sheet
        Sheet sheet = workbook.getSheetAt(0);
        if (sheet != null) {
            // 4. 有內容的最後一列index
            int lastRowNumber = sheet.getLastRowNum();
            // 第1列為標題,從第2列開始讀取
            for (int i = 2; i <= lastRowNumber; i++) {
                // 5. 取得row
                Row row = sheet.getRow(i);
                StudentCourseScoreReportModel model = new StudentCourseScoreReportModel();
                
                for (int j = 0; j < columnNames.length; j++) {
                    if (row != null) {
                        // 6. 取得cell
                        Cell cell = row.getCell(j);
                        if (cell == null) {
                            continue;
                        }
                        // 7. 按照欄位名稱取值,放入model
                        switch (columnNames[j]) {
                            case "學號":
                                if (cell.getCellType() == CellType.STRING) {
                                    String studentNumber = cell.getStringCellValue();
                                    if (studentNumber != null) {
                                        model.setStudentNumber(studentNumber);
                                    }
                                }
                                break;
                            case "科系":
                                if (cell.getCellType() == CellType.STRING) {
                                    String departmentDesc = cell.getStringCellValue();
                                    if (departmentDesc != null) {
                                        model.setDepartmentDesc(departmentDesc);
                                    }
                                }
                                break;
                            case "年級":
                                if (cell.getCellType() == CellType.STRING) {
                                    String grade = cell.getStringCellValue();
                                    if (grade != null) {
                                        model.setGrade(grade);
                                    }
                                }
                                break;
                            case "姓名":
                                if (cell.getCellType() == CellType.STRING) {
                                    String fullName = cell.getStringCellValue();
                                    if (fullName != null) {
                                        model.setFullName(fullName);
                                    }
                                }
                                break;
                            case "課程":
                                if (cell.getCellType() == CellType.STRING) {
                                    String courseDesc = cell.getStringCellValue();
                                    if (courseDesc != null) {
                                        model.setCourseDesc(courseDesc);
                                    }
                                }
                                break;
                            case "成績":
                                if (cell.getCellType() == CellType.NUMERIC) {
                                    Double score = cell.getNumericCellValue();
                                    if (score != null) {
                                        model.setScore(score.intValue());
                                    }
                                }
                                break;
                            case "考試日期":
                                if (cell.getCellType() == CellType.STRING) {
                                    String testDate = cell.getStringCellValue();
                                    if (testDate != null) {
                                        model.setTestDate(testDate);
                                    }
                                }
                                break;
                            default:
                                break;
                        }
                    }
                }
                modelList.add(model);
            }
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
執行到最後就能拿到List<StudentCourseScoreReportModel>資料了
▲補充
Workbook介面有繼承Iterable<Sheet>;Sheet介面有繼承Iterable<Row>;Row介面有繼承Iterable<Cell>。表示除了範例使用的遍歷方式外,可以直接以foreach遍歷資料。
Workbook workbook = WorkbookFactory.create(inputStream);
for (Sheet sheet : workbook) {
    for (Row row : sheet) {
        for (Cell cell : row) {
        }
    }
}
Apache POI讀取Excel比較繁瑣的部分就是判斷Cell中資料的型別或格式,如果要寫得更謹慎就要多一些例外處理,才能判斷日期或數字格式是否正確,不然cell.getNumericCellValue()或cell.getDateCellValue()都滿容易有Exception的狀況發生。