Written By: Hoàng Mạnh Cường (Java 10)
Gmail: manhcuong200997@gmail.com
Bài viết gốc: https://www.baeldung.com/java-read-dates-excel

1. Tổng quan

Khi nhắc đến các file Microsoft Excel, việc đọc giá trị từ các ô khác nhau có thể sẽ có một chút khó khăn. Các file Excel là những bảng tính được xây dựng theo các hàng và ô, có thể chứa kiểu dữ liệu là String, Numeric, Date, Boolean hoặc thậm chí cả công thức tính (Formula). Apache POI là thư viện cung cấp đầy đủ bộ công cụ để xử lý các tệp excel và các loại giá trị khác nhau.

Trong bài viết này, chúng ta sẽ tập trung tìm hiểu cách xử lý các file excel, lặp qua các hàng và ô và sử dụng cách thích hợp để đọc từng loại giá trị trong các ô.

2. Maven Dependency

Hãy bắt đầu bằng việc thêm Apache POI dependency vào file pom.xml:

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.0</version>
</dependency>

Có thể xem phiên bản mới nhất của poi-ooxml tại Maven Central.

3. Tổng quan về Apache POI

Hệ thống phân cấp bắt đầu với workbook, đại diện cho toàn bộ file Excel. Mỗi file có thể chứa một hoặc nhiều sheet, là tập hợp bao gồm các hàng và các ô. Tùy vào phiên bản của file Excel, HSSF là tiền tố của các lớp đại diện cho các file Excel trước đây(.xls), trong khi XSSF được sử dụng cho những phiên bản mới nhất (.xlxs). Do đó, chúng ta có như sau:

  • Các lớp XSSFWorkbookHSSFWorkbook đại diện cho Excel workbook.
  • Sheet interface đại diện cho Excel worksheets.
  • Row interface đại diện cho các hàng.
  • Cell interface đại diện cho các ô.

3.1. Xử lý Excel Files

Đầu tiên, chúng ta mở file muốn đọc và chuyển đổi thành FileInputStream để tiếp tục xử lý. FileInputStream constructor ném ra ngoại lệ java.io.FileNotFoundException nên cần phải triển khai trong khối try-catch và đóng stream khi kết thúc:

public static void readExcel(String filePath) {
    File file = new File(filePath);
    try {
        FileInputStream inputStream = new FileInputStream(file);
        ...
        inputStream.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

3.2. Lặp qua Excel File

Sau khi mở thành công InputStream, chúng ta sẽ tạo ra XSSFWorkbook và lặp qua mỗi hàng và ô của từng sheet. Trong trường hợp chúng ta biết chính xác số lượng sheet hoặc tên của một sheet cụ thể, chúng ta có thể sử dụng các phương thức getSheetAt(int index)getSheet(String sheetName) của XSSFWorkbook tương ứng.

Khi muốn đọc bất kì loại file Excel nào, chúng ta sẽ lặp qua tất cả các sheet bằng cách sử dụng 3 vòng lặp for lồng nhau, 1 vòng cho các sheet, 1 vòng cho các hàng của sheet và vòng cuối cùng là cho các ô của mỗi sheet.

Để thấy rõ được mục tiêu của bài viết này đem lại, chúng ta sẽ in dữ liệu ra console:

FileInputStream inputStream = new FileInputStream(file);
Workbook baeuldungWorkBook = new XSSFWorkbook(inputStream);
for (Sheet sheet : baeuldungWorkBook) {
...
}

Sau đó, để lặp qua các hàng trong sheet, chúng ta cần phải tìm index của hàng đầu tiên và hàng cuối cùng nhận được từ đối tượng của sheet:

int firstRow = sheet.getFirstRowNum();
int lastRow = sheet.getLastRowNum();
for (int index = firstRow + 1; index <= lastRow; index++) {
    Row row = sheet.getRow(index);
}

Cuối cùng, chúng ta sẽ thực hiện tương tự với các ô. Ngoài ra, khi truy cập từng ô, chúng ta có thể tùy chọn truyền một MissingCellPolicy về cơ bản cho POI biết những gì được trả về khi giá trị của cột là rỗng hoặc null. MissingCellPolicy enum chứ ba giá trị được liệt kê:

  • RETURN_NULL_AND_BLANK
  • RETURN_BLANK_AS_NULL
  • CREATE_NULL_AS_BLANK

Code cho việc lặp lại qua ô như sau:

for (int cellIndex = row.getFirstCellNum(); cellIndex < row.getLastCellNum(); cellIndex++) {
    Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
    ...
}

3.3. Đọc giá trị của ô trong Excel

Như đã đề cập trước đó, các hàng của Microsoft Excel có thể bao gồm nhiều kiểu giá trị khác nhau, vì vậy đó là điều quan trọng trong việc phân biệt một kiểu giá trị của ô này với kiểu giá trị khác và sử dụng phương thức phù hợp để trích xuất giá trị. Phía dưới là danh sách các kiểu giá trị:

  • NONE
  • NUMERIC
  • STRING
  • FORMULA
  • BLANK
  • BOOLEAN
  • ERROR

Chúng ta luôn tập trung vào 4 kiểu chính: Numeric, String, Boolean, và Formula, trong đó kiểu cuối cùng chứa giá trị tính toán thuộc 3 kiểu đầu tiên.

Hãy tạo ra một phương thức hỗ trợ cơ bản có thể kiểm tra từng kiểu giá trị và dựa vào đó nó sẽ sử dụng phương thức phù hợp để truy cập vào giá trị. Cũng có thể coi giá trị của ô là một Chuỗi và truy xuất nó bằng phương thức tương ứng.

Có 2 điều rất quan trọng cần lưu ý. Đầu tiên, giá trị Date được lưu trữ dưới dạng các giá trị Numeric, và nếu giá trị của ô là kiểu FORMULA thì chúng ta cần sử dụng phương thức getCachedFormulaResultType() thay vì getCellType() để kiểm tra kết quả tính toán của công thức:

public static void printCellValue(Cell cell) {
    CellType cellType = cell.getCellType().equals(CellType.FORMULA)
      ? cell.getCachedFormulaResultType() : cell.getCellType();
    if (cellType.equals(CellType.STRING)) {
        System.out.print(cell.getStringCellValue() + " | ");
    }
    if (cellType.equals(CellType.NUMERIC)) {
        if (DateUtil.isCellDateFormatted(cell)) {
            System.out.print(cell.getDateCellValue() + " | ");
        } else {
            System.out.print(cell.getNumericCellValue() + " | ");
        }
    }
    if (cellType.equals(CellType.BOOLEAN)) {
        System.out.print(cell.getBooleanCellValue() + " | ");
    }
}

Bây giờ, tất cả những gì chúng ta cần là gọi phương thức printCellValue trong vòng lặp ô và như vậy là công việc đã hoàn tất. Đây là một đoạn mã nằm trong code đầy đủ:

...
for (int cellIndex = row.getFirstCellNum(); cellIndex < row.getLastCellNum(); cellIndex++) {
    Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
    printCellValue(cell);
}
...

4. Kết luận

Trong bài viết này, chúng ta đã được xem một dự án mẫu về đọc file Excel và truy cập các giá trị ô khác nhau bằng cách sử dụng Apache POI.

Các bạn có thể tham khảo source code đầy đủ tại Reading Values From Excel

Bài viết của mình tới đây là kết thúc. Hi vọng những thông tin trong bài viết này có thể hỗ trợ tốt cho các bạn trong công việc hiện tại và trong tương lai.

Thanks for watching!