1. Kiến Trúc và Các Thành Phần Cốt Lõi của Apache POI
Apache POI là một thư viện mã nguồn mở mạnh mẽ do Apache Software Foundation phát triển, cung cấp API cho phép các ứng dụng Java đọc, ghi và thao tác với các định dạng tệp tin Microsoft Office. Đối với hệ sinh thái .NET, thư viện tương đương là NPOI.
Các Module Chính
- HSSF (Horrible SpreadSheet Format): Xử lý định dạng Excel cũ (
.xls- Excel 97-2003). - XSSF (XML SpreadSheet Format): Xử lý định dạng Excel mới (
.xlsx- Excel 2007+ dựa trên OOXML). - HWPF / XWPF: Thao tác với tài liệu Word (
.docvà.docx). - HSLF / XSLF: Thao tác với PowerPoint.
- HDGF / HSMF: Xử lý Visio và Outlook.
Các Interface và Class Thường Dùng
Để viết mã linh hoạt và dễ dàng chuyển đổi giữa .xls và .xlsx, bạn nên sử dụng các interface chung thay vì các class cụ thể của HSSF hay XSSF:
| Interface / Class | Chức Năng |
|---|---|
Workbook | Đại diện cho toàn bộ tài liệu Excel. |
Sheet | Đại diện cho một trang tính (Worksheet). |
Row | Đại diện cho một hàng dữ liệu. |
Cell | Đại diện cho một ô dữ liệu. |
CellStyle | Quản lý định dạng (màu sắc, viền, font chữ). |
CreationHelper | Factory tạo ra các đối tượng phức tạp như công thức, hyperlink. |
2. Các Thao Tác Cơ Bản Với Excel
Khởi Tạo Workbook và Sheet
Dưới đây là cách tạo một file Excel mới, thêm trang tính và ghi dữ liệu ra ổ đĩa. Mã nguồn được tối ưu hóa bằng try-with-resources để tự động đóng luồng.
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExcelCreator {
public static void main(String[] args) {
String targetPath = "output/financial_report.xlsx";
// Sử dụng XSSFWorkbook cho định dạng .xlsx
try (Workbook workbook = new XSSFWorkbook();
FileOutputStream fileOut = new FileOutputStream(targetPath)) {
Sheet reportSheet = workbook.createSheet("DoanhThu_Q1");
Sheet summarySheet = workbook.createSheet("TongHop");
workbook.write(fileOut);
System.out.println("Tạo file Excel thành công!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ghi Dữ Liệu Vào Ô (Cell)
Các kiểu dữ liệu như chuỗi, số, boolean và ngày tháng đều có thể được gán trực tiếp vào ô.
Sheet dataSheet = workbook.createSheet("DuLieu");
Row headerRow = dataSheet.createRow(0);
// Tạo và gán giá trị chuỗi
Cell nameCell = headerRow.createCell(0);
nameCell.setCellValue("HoTen");
// Tạo và gán giá trị boolean, số thực
headerRow.createCell(1).setCellValue("IsActive");
headerRow.createCell(2).setCellValue(98.75);
Thiết Lập Metadata (Thuộc Tính Tài Liệu)
Thêm thông tin tác giả, tiêu đề giúp quản lý tệp tin chuyên nghiệp hơn:
import org.apache.poi.ooxml.POIXMLProperties;
POIXMLProperties props = ((XSSFWorkbook) workbook).getProperties();
props.getCoreProperties().setCreator("HeThongERP");
props.getCoreProperties().setTitle("BaoCaoTaiChinh");
props.getExtendedProperties().getUnderlyingProperties().setCompany("TechCorp VietNam");
Tạo Chú Thích (Comment)
Drawing> drawing = dataSheet.createDrawingPatriarch();
CreationHelper factory = workbook.getCreationHelper();
// Định vị trí comment (col1, row1, col2, row2)
ClientAnchor anchor = factory.createClientAnchor();
anchor.setCol1(2); anchor.setRow1(1);
anchor.setCol2(5); anchor.setRow2(4);
Comment cellComment = drawing.createCellComment(anchor);
cellComment.setString(factory.createRichTextString("Doanh thu chưa bao gồm VAT"));
cellComment.setAuthor("KeToanTruong");
Cell targetCell = dataSheet.createRow(1).createCell(2);
targetCell.setCellValue(5000000);
targetCell.setCellComment(cellComment);
3. Định Dạng và Thao Tác Nâng Cao Với Ô
Lưu ý quan trọng cho Kỹ sư: Luôn tạo CellStyle ở cấp độ Workbook và tái sử dụng chúng. Việc tạo style mới cho từng ô sẽ dẫn đến lỗi tràn bộ nhớ (OutOfMemory) đối với file lớn.
Định Dạng Dữ Liệu (Format)
CellStyle currencyStyle = workbook.createCellStyle();
CreationHelper createHelper = workbook.getCreationHelper();
// Định dạng tiền tệ Việt Nam
currencyStyle.setDataFormat(createHelper.createDataFormat().getFormat("#,##0 VND"));
Row moneyRow = dataSheet.createRow(2);
Cell priceCell = moneyRow.createCell(0);
priceCell.setCellValue(15000000);
priceCell.setCellStyle(currencyStyle);
Gộp Ô (Merge Cells)
Sử dụng CellRangeAddress để gộp các ô liền kề. Tham số lần lượt là: (firstRow, lastRow, firstCol, lastCol).
import org.apache.poi.ss.util.CellRangeAddress;
Row titleRow = dataSheet.createRow(3);
titleRow.createCell(0).setCellValue("BAO CAO TONG HOP");
// Gộp từ cột 0 đến cột 3 của hàng 3
dataSheet.addMergedRegion(new CellRangeAddress(3, 3, 0, 3));
Căn Chỉnh, Viền và Font Chữ
CellStyle headerStyle = workbook.createCellStyle();
// Căn giữa
headerStyle.setAlignment(HorizontalAlignment.CENTER);
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
// Thiết lập viền
headerStyle.setBorderTop(BorderStyle.THIN);
headerStyle.setBorderBottom(BorderStyle.MEDIUM);
headerStyle.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
// Cấu hình Font
Font headerFont = workbook.createFont();
headerFont.setFontName("Arial");
headerFont.setBold(true);
headerFont.setFontHeightInPoints((short) 12);
headerFont.setColor(IndexedColors.WHITE.getIndex());
headerStyle.setFont(headerFont);
// Nền màu
headerStyle.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// Áp dụng style
titleRow.getCell(0).setCellStyle(headerStyle);
Điều Chỉnh Kích Thước
Chiều rộng cột trong POI được tính bằng 1/256 độ rộng ký tự. Chiều cao hàng tính bằng points.
dataSheet.setColumnWidth(0, 30 * 256); // Rộng 30 ký tự
titleRow.setHeightInPoints(25); // Cao 25 points
4. Tích Hợp Công Thức (Formulas)
POI hỗ trợ ghi trực tiếp các chuỗi công thức Excel tiêu chuẩn vào ô.
Các Công Thức Thông Dụng
Row formulaRow = dataSheet.createRow(5);
formulaRow.createCell(0).setCellValue(150);
formulaRow.createCell(1).setCellValue(200);
Cell sumCell = formulaRow.createCell(2);
sumCell.setCellFormula("SUM(A6:B6)"); // Tính tổng
Cell ifCell = formulaRow.createCell(3);
ifCell.setCellFormula("IF(C6>300, \"Dat\", \"Khong Dat\")"); // Logic IF
Cell lookupCell = formulaRow.createCell(4);
lookupCell.setCellFormula("VLOOKUP(A6, $A$1:$C$4, 3, FALSE)");
Đánh Giá Công Thức (Formula Evaluation)
Để lấy kết quả tính toán của công thức ngay trong Java mà không cần mở bằng Excel:
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
CellValue evaluatedValue = evaluator.evaluate(sumCell);
System.out.println("Ket qua cua SUM: " + evaluatedValue.getNumberValue());
5. Đồ Họa và Hình Ảnh
Vẽ Các Hình Khối Cơ Bản
Sử dụng ClientAnchor để neo vị trí hình vẽ dựa trên tọa độ ô.
Drawing> drawingPatriarch = dataSheet.createDrawingPatriarch();
ClientAnchor shapeAnchor = new XSSFClientAnchor(0, 0, 0, 0, 2, 10, 5, 15);
SimpleShape rectangle = drawingPatriarch.createSimpleShape(shapeAnchor);
rectangle.setShapeType(ShapeTypes.RECTANGLE);
rectangle.setLineStyleColor(255, 0, 0); // Viền đỏ
rectangle.setFillColor(200, 200, 200); // Nền xám
rectangle.setLineWidth(2.5);
Chèn Ảnh Vào Excel
import java.io.InputStream;
import org.apache.poi.util.IOUtils;
try (InputStream is = ExcelCreator.class.getResourceAsStream("/logo.png")) {
byte[] bytes = IOUtils.toByteArray(is);
int pictureIdx = workbook.addPicture(bytes, Workbook.PICTURE_TYPE_PNG);
ClientAnchor imgAnchor = new XSSFClientAnchor();
imgAnchor.setCol1(1); imgAnchor.setRow1(1);
imgAnchor.setCol2(3); imgAnchor.setRow2(6);
Picture picture = drawingPatriarch.createPicture(imgAnchor, pictureIdx);
// picture.resize(); // Tự động điều chỉnh kích thước ảnh
}
6. Quản Lý Sheet và Cấu Trúc Bảng
Đổi Tên và Chọn Sheet Mặc Định
workbook.setSheetName(0, "BaoCao_Chinh");
workbook.setActiveSheet(0); // Đặt làm sheet mặc định khi mở file
Cố Định Hàng/Cột (Freeze Panes)
Giúp tiêu đề luôn hiển thị khi cuộn trang. Tham số: (colSplit, rowSplit).
// Cố định hàng đầu tiên (Tiêu đề cột)
dataSheet.createFreezePane(0, 1);
Ẩn/Hiện Lưới và Grouping
dataSheet.setDisplayGridlines(false); // Tắt đường kẻ ô
// Nhóm các hàng từ 5 đến 10 lại (có nút +/- để thu gọn)
dataSheet.groupRow(4, 9);
7. Tính Năng Bảo Mật và Tương Tác
Khóa Sheet Bằng Mật Khẩu
CellStyle unlockedStyle = workbook.createCellStyle();
unlockedStyle.setLocked(false);
Cell editableCell = dataSheet.createRow(20).createCell(0);
editableCell.setCellValue("Co the chinh sua");
editableCell.setCellStyle(unlockedStyle);
// Khóa toàn bộ sheet, ngoại trừ các ô được set unlockedStyle
dataSheet.protectSheet("MatKhauBiMat123");
Data Validation (Tạo Dropdown List)
Giới hạn dữ liệu đầu vào bằng danh sách thả xuống.
DataValidationHelper validationHelper = dataSheet.getDataValidationHelper();
DataValidationConstraint constraint = validationHelper.createExplicitListConstraint(
new String[]{"Ha Noi", "Ho Chi Minh", "Da Nang"}
);
CellRangeAddressList addressList = new CellRangeAddressList(1, 100, 0, 0); // Cột A, hàng 2 đến 101
DataValidation validation = validationHelper.createValidation(constraint, addressList);
validation.createErrorBox("Loi Du Lieu", "Vui long chon thanh pho hop le!");
dataSheet.addValidationData(validation);
Chèn Hyperlink
CreationHelper linkHelper = workbook.getCreationHelper();
Hyperlink link = linkHelper.createHyperlink(HyperlinkType.URL);
link.setAddress("https://github.com/apache/poi");
Cell linkCell = dataSheet.createRow(22).createCell(0);
linkCell.setCellValue("Truy cap POI Repository");
linkCell.setHyperlink(link);
8. Thao Tác Cơ Bản Với Word (Apache XWPF)
Mặc dù POI nổi tiếng với Excel, module XWPF cũng cung cấp khả năng xử lý file .docx.
Tạo Tài Liệu Word Mới
import org.apache.poi.xwpf.usermodel.*;
import java.io.FileOutputStream;
try (XWPFDocument document = new XWPFDocument();
FileOutputStream out = new FileOutputStream("contract.docx")) {
// Tạo đoạn văn
XWPFParagraph paragraph = document.createParagraph();
paragraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun run = paragraph.createRun();
run.setText("HOP DONG LAO DONG");
run.setBold(true);
run.setFontSize(16);
run.setColor("0000FF");
// Tạo bảng biểu
XWPFTable table = document.createTable(3, 3);
table.getRow(0).getCell(0).setText("STT");
table.getRow(0).getCell(1).setText("Noi Dung");
table.getRow(0).getCell(2).setText("Gia Tri");
document.write(out);
}
Trích Xuất Văn Bản Từ Word
import java.io.FileInputStream;
try (FileInputStream fis = new FileInputStream("contract.docx");
XWPFDocument doc = new XWPFDocument(fis)) {
// Đọc tất cả các đoạn văn
for (XWPFParagraph p : doc.getParagraphs()) {
System.out.println(p.getText());
}
// Đọc dữ liệu từ bảng
for (XWPFTable tbl : doc.getTables()) {
for (XWPFTableRow row : tbl.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
System.out.print(cell.getText() + "\t");
}
System.out.println();
}
}
}