Đôi khi chúng ta cần đọc dữ liệu hoặc tạo báo cáo ở định dạng Excel, chủ yếu cho mục đích kinh doanh hoặc tài chính. Vì Java không có công cụ tích hợp sẵn để làm việc với các file Excel nên chúng ta sẽ cần dùng các API mã nguồn mở cho mục đích này.
Cụ thể, JExcel và Apache POI là hai lựa chọn được cộng đồng Java đề xuất nhiều nhất. Sau khi tìm hiểu kỹ hơn, chúng tôi nhận thấy Apache POI là lựa chọn tối hơn ưu vì một vài lý do:
- Được hậu thuẫn bởi tổ chức Apache Foundation.
- JExcel không hỗ trợ định dạng xlsx, trong khi đó POI hỗ trợ cả định dạng xls lẫn xlsx.
- Apache POI cung cấp cơ chế xử lý dựa trên stream (stream-based processing), phù hợp với các file lớn và cần ít bộ nhớ hơn.
Apache POI
Apache POI hỗ trợ các tài liệu Microsoft Excel một cách tuyệt vời với khả năng xử lý cả hai định dạng bảng tính là xls và xlsx. Dưới đây là một số điểm quan trọng về API này:
- Apache POI bao gồm triển khai HSSF cho định dạng file Excel ’97(-2007), tức là .xls.
- Triển khai XSSF của Apache POI nên được dùng cho định dạng file Excel 2007 OOXML (.xlsx).
- API HSSF và XSSF của Apache POI cung cấp các cơ chế đọc, ghi và chỉnh sửa bảng tính Excel.
- Apache POI cũng cung cấp SXSSF API, một extension của XSSF để làm việc với các sheet Excel rất lớn. SXSSF API cần ít bộ nhớ hơn và phù hợp hơn khi làm việc với các bảng tính rất lớn và bộ nhớ heap có hạn.
- Có hai model (cấu trúc biểu diễn dữ liệu và logic) để lựa chọn: event model và user model. Event model cần ít bộ nhớ hơn vì file Excel được đọc dưới dạng các token cần được phải xử lý. User model có tính hướng đối tượng cao hơn, dễ sử dụng hơn, và chúng ta sẽ dùng model này trong các ví dụ ở dưới.
- Apache POI hỗ trợ tốt các tính năng bổ sung của Excel như làm việc với công thức, tạo format cho cell bằng cách tô màu và kẻ viền, tùy chỉnh font, header và footer, xác thực dữ liệu, chèn hình ảnh, hyperlink, v.v.
Các dependency của Maven cho Apache POI
Nếu bạn đang sử dụng Maven, hãy thêm các dependency dưới đây cho Apache POI:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.10-FINAL</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.10-FINAL</version>
</dependency>
Phiên bản Apache POI tại thời điểm viết bài là 3.10-FINAL. Nếu bạn có ứng dụng Java chạy riêng, thêm các file JAR trong hình dưới đây.
Ví dụ Apache POI: Đọc file Excel
Giả sử chúng ta có một file Excel tên là “Sample.xlsx” gồm hai sheet và chứa dữ liệu như hình minh họa bên dưới. Chúng ta muốn đọc file Excel này và tạo ra một danh sách các đối tượng Country
. Sheet1 có một số dữ liệu không liên quan. Chúng ta sẽ bỏ qua phần dữ liệu này khi đọc nó.
Code cho Java bean Country.java
của chúng ta như sau:
package com.journaldev.excel.read;
public class Country {
private String name;
private String shortCode;
public Country(String n, String c){
this.name=n;
this.shortCode=c;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getShortCode() {
return shortCode;
}
public void setShortCode(String shortCode) {
this.shortCode = shortCode;
}
@Override
public String toString(){
return name + "::" + shortCode;
}
}
Chương trình ví dụ sử dụng Apache POI để đọc file Excel và lưu dữ liệu vào danh sách các đối tượng Country
có code như dưới:
package com.journaldev.excel.read;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ReadExcelFileToList {
public static List<Country> readExcelData(String fileName) {
List<Country> countriesList = new ArrayList<Country>();
try {
//Create the input stream from the xlsx/xls file
FileInputStream fis = new FileInputStream(fileName);
//Create Workbook instance for xlsx/xls file input stream
Workbook workbook = null;
if(fileName.toLowerCase().endsWith("xlsx")){
workbook = new XSSFWorkbook(fis);
}else if(fileName.toLowerCase().endsWith("xls")){
workbook = new HSSFWorkbook(fis);
}
//Get the number of sheets in the xlsx file
int numberOfSheets = workbook.getNumberOfSheets();
//loop through each of the sheets
for(int i=0; i < numberOfSheets; i++){
//Get the nth sheet from the workbook
Sheet sheet = workbook.getSheetAt(i);
//every sheet has rows, iterate over them
Iterator<Row> rowIterator = sheet.iterator();
while (rowIterator.hasNext())
{
String name = "";
String shortCode = "";
//Get the row object
Row row = rowIterator.next();
//Every row has columns, get the column iterator and iterate over them
Iterator<Cell> cellIterator = row.cellIterator();
while (cellIterator.hasNext())
{
//Get the Cell object
Cell cell = cellIterator.next();
//check the cell type and process accordingly
switch(cell.getCellType()){
case Cell.CELL_TYPE_STRING:
if(shortCode.equalsIgnoreCase("")){
shortCode = cell.getStringCellValue().trim();
}else if(name.equalsIgnoreCase("")){
//2nd column
name = cell.getStringCellValue().trim();
}else{
//random data, leave it
System.out.println("Random data::"+cell.getStringCellValue());
}
break;
case Cell.CELL_TYPE_NUMERIC:
System.out.println("Random data::"+cell.getNumericCellValue());
}
} //end of cell iterator
Country c = new Country(name, shortCode);
countriesList.add(c);
} //end of rows iterator
} //end of sheets for loop
//close file input stream
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return countriesList;
}
public static void main(String args[]){
List<Country> list = readExcelData("Sample.xlsx");
System.out.println("Country List\\n"+list);
}
}
Chương trình này gồm các bước chính sau:
- Tạo instance
Workbook
phù hợp với loại file:XSSFWorkbook
cho định dạng .xlsx vàHSSFWorkbook
cho định dạng .xls. Lưu ý rằng chúng ta có thể tạo một lớp wrapper sử dụng Factory pattern để lấy instanceWorkbook
dựa trên tên file. - Sử dụng phương thức
getNumberOfSheets()
củaWorkbook
để lấy tổng số sheet, sau đó dùng vòng lặpfor
để duyệt qua từng sheet. Lấy instanceSheet
bằng phương thứcgetSheetAt(int i)
. - Lấy iterator cho
Row
rồi từ đó lấy iterator choCell
để truy cập đối tượngCell
. Ở đây Apache POI sử dụng Iterator pattern. - Sử dụng
switch-case
để xác định kiểu dữ liệu củaCell
và xử lý sao cho phù hợp.
Khi chạy chương trình ví dụ Apache POI ở trên, output trên console sẽ như sau:
Random data::1.0
Random data::2.0
Random data::3.0
Random data::4.0
Country List
[India::IND, Afghanistan::AFG, United States of America::USA, Anguilla::AIA,
Denmark ::DNK, Dominican Republic ::DOM, Algeria ::DZA, Ecuador ::ECU]
Ví dụ Apache POI: Ghi file Excel
Việc ghi file Excel bằng Apache POI cũng tương tự như việc đọc, chỉ khác là ở đây chúng ta cần tạo Workbook
trước. Sau đó, ta tạo các sheet, row, cell và gán giá trị cho chúng, rồi dùng FileOutputStream
để ghi Workbook
ra file.
Chúng ta hãy xem một ví dụ Apache POI đơn giản, trong đó ta sẽ sử dụng danh sách các đối tượng Country
từ ví dụ đọc ở trên để lưu vào một sheet của một file Excel khác.
package com.journaldev.excel.read;
import java.io.FileOutputStream;
import java.util.Iterator;
import java.util.List;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class WriteListToExcelFile {
public static void writeCountryListToFile(String fileName, List<Country> countryList) throws Exception{
Workbook workbook = null;
if(fileName.endsWith("xlsx")){
workbook = new XSSFWorkbook();
}else if(fileName.endsWith("xls")){
workbook = new HSSFWorkbook();
}else{
throw new Exception("invalid file name, should be xls or xlsx");
}
Sheet sheet = workbook.createSheet("Countries");
Iterator<Country> iterator = countryList.iterator();
int rowIndex = 0;
while(iterator.hasNext()){
Country country = iterator.next();
Row row = sheet.createRow(rowIndex++);
Cell cell0 = row.createCell(0);
cell0.setCellValue(country.getName());
Cell cell1 = row.createCell(1);
cell1.setCellValue(country.getShortCode());
}
//lets write the excel data to file now
FileOutputStream fos = new FileOutputStream(fileName);
workbook.write(fos);
fos.close();
System.out.println(fileName + " written successfully");
}
public static void main(String args[]) throws Exception{
List<Country> list = ReadExcelFileToList.readExcelData("Sample.xlsx");
WriteListToExcelFile.writeCountryListToFile("Countries.xls", list);
}
}
Khi chạy ví dụ ở trên, file Excel được tạo ra sẽ có dạng như hình minh họa bên dưới.
Ví dụ Apache POI: Đọc công thức trong Excel
Đôi khi chúng ta cần xử lý các file Excel phức tạp chứa nhiều công thức. Hãy cùng xem một ví dụ Apache POI đơn giản để đọc nội dung công thức của một cell cũng như giá trị được tính toán từ công thức đó.
package com.journaldev.excel.read;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ReadExcelFormula {
public static void readExcelFormula(String fileName) throws IOException{
FileInputStream fis = new FileInputStream(fileName);
//assuming xlsx file
Workbook workbook = new XSSFWorkbook(fis);
Sheet sheet = workbook.getSheetAt(0);
Iterator<Row> rowIterator = sheet.iterator();
while (rowIterator.hasNext())
{
Row row = rowIterator.next();
Iterator<Cell> cellIterator = row.cellIterator();
while (cellIterator.hasNext())
{
Cell cell = cellIterator.next();
switch(cell.getCellType()){
case Cell.CELL_TYPE_NUMERIC:
System.out.println(cell.getNumericCellValue());
break;
case Cell.CELL_TYPE_FORMULA:
System.out.println("Cell Formula="+cell.getCellFormula());
System.out.println("Cell Formula Result Type="+cell.getCachedFormulaResultType());
if(cell.getCachedFormulaResultType() == Cell.CELL_TYPE_NUMERIC){
System.out.println("Formula Value="+cell.getNumericCellValue());
}
}
}
}
}
public static void main(String args[]) throws IOException {
readExcelFormula("FormulaMultiply.xlsx");
}
}
Khi chạy ví dụ ở trên, chúng ta sẽ nhận được output như sau:
1.0
2.0
3.0
4.0
Cell Formula=A1*A2*A3*A4
Cell Formula Result Type=0
Formula Value=24.0
Ví dụ Apache POI: Ghi công thức vào Excel
Đôi khi, chúng ta cần thực hiện một số phép tính rồi mới ghi giá trị vào cell. Ta có thể sử dụng công thức của Excel để thực hiện việc tính toán này. Cách này sẽ giúp đảm bảo tính chính xác cao hơn, vì giá trị của cell chứa công thức sẽ tự động cập nhật nếu các cell tham chiếu trong công thức bị thay đổi.
Hãy xem một ví dụ đơn giản về cách ghi công thức vào file Excel bằng Apache POI API.
package com.journaldev.excel.read;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class WriteExcelWithFormula {
public static void writeExcelWithFormula(String fileName) throws IOException{
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Numbers");
Row row = sheet.createRow(0);
row.createCell(0).setCellValue(10);
row.createCell(1).setCellValue(20);
row.createCell(2).setCellValue(30);
//set formula cell
row.createCell(3).setCellFormula("A1*B1*C1");
//lets write to file
FileOutputStream fos = new FileOutputStream(fileName);
workbook.write(fos);
fos.close();
System.out.println(fileName + " written successfully");
}
public static void main(String[] args) throws IOException {
writeExcelWithFormula("Formulas.xlsx");
}
}
File Excel được tạo ra từ chương trình trên sẽ có dạng như hình bên dưới.
Trên đây là toàn bộ nội dung hướng dẫn về Apache POI để làm việc với file Excel. Bạn có thể tìm hiểu thêm các tính năng khác của Apache POI bằng cách xem qua tài liệu chính thức về các class và method của thư viện này. Chúc bạn nhanh chóng làm chủ Apache POI và các khả năng mà nó mang lại.