In the Java ecosystem, dealing with Microsoft Office documents is a ubiquitous requirement. Whether you are generating financial reports, exporting data grids, or parsing user uploads, Apache POI is the de facto standard library for the job.
While POI supports Word, PowerPoint, and Outlook, this post focuses on the most common use case: Excel (Spreadsheets).

1. Understanding the Architecture
Before writing code, it is crucial to understand which API to use, as Excel files come in two main flavors:
- HSSF (Horrible Spreadsheet Format): Used for the older binary format (.xls - Excel '97(-2007)).
- XSSF (XML Spreadsheet Format): Used for the newer OpenXML format (.xlsx - Excel 2007+).
Note: For most modern applications, you will be working with XSSF (.xlsx).
2. Setting Up Your Project
To get started, you need to add the dependencies to your project. If you are using Maven, add the following to your pom.xml. Note that we need poi-ooxml to handle .xlsx files.
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
</dependencies>
3. Writing to an Excel File
The object hierarchy in POI mirrors the physical structure of an Excel file: Workbook -> Sheet -> Row -> Cell
Here is a complete example of creating a sales report with header styling.
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExcelWriter {
public static void main(String[] args) {
// 1. Create the Workbook
try (Workbook workbook = new XSSFWorkbook()) {
// 2. Create the Sheet
Sheet sheet = workbook.createSheet("Monthly Sales");
// 3. Create a Font and CellStyle for the header
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerFont.setFontHeightInPoints((short) 14);
headerFont.setColor(IndexedColors.RED.getIndex());
CellStyle headerCellStyle = workbook.createCellStyle();
headerCellStyle.setFont(headerFont);
// 4. Create the Header Row
Row headerRow = sheet.createRow(0);
String[] columns = {"Product Name", "Quantity", "Price"};
/* NOTE: The '<' below must be written as '<' in HTML */
for (int i = 0; i < columns.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(columns[i]);
cell.setCellStyle(headerCellStyle);
}
// 5. Add Data
Row row = sheet.createRow(1);
row.createCell(0).setCellValue("Laptop");
row.createCell(1).setCellValue(5);
row.createCell(2).setCellValue(1200.00);
// 6. Resize columns to fit content
/* NOTE: The '<' below must be written as '<' in HTML */
for (int i = 0; i < columns.length; i++) {
sheet.autoSizeColumn(i);
}
// 7. Write the output to a file
try (FileOutputStream fileOut = new FileOutputStream("sales_report.xlsx")) {
workbook.write(fileOut);
}
System.out.println("Excel file created successfully!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. Reading from an Excel File
Reading is slightly more complex because you must handle different data types (e.g., a cell might look like a number but contain a string).
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class ExcelReader {
public static void main(String[] args) {
// Open the file using try-with-resources to ensure it closes automatically
try (FileInputStream file = new FileInputStream(new File("sales_report.xlsx"));
Workbook workbook = new XSSFWorkbook(file)) {
// Get the first sheet (index 0)
Sheet sheet = workbook.getSheetAt(0);
// Iterate through rows
for (Row row : sheet) {
// Iterate through cells
for (Cell cell : row) {
// distinct handling for different data types
switch (cell.getCellType()) {
case STRING:
System.out.print(cell.getStringCellValue() + "\t");
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
System.out.print(cell.getDateCellValue() + "\t");
} else {
System.out.print(cell.getNumericCellValue() + "\t");
}
break;
case BOOLEAN:
System.out.print(cell.getBooleanCellValue() + "\t");
break;
default:
System.out.print(" \t");
}
}
System.out.println(); // New line after every row
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. Critical Best Practices
The Memory Trap
A common pitfall is OutOfMemoryError. The standard XSSFWorkbook stores the entire spreadsheet in memory (DOM-like approach). If you try to write 100,000 rows, your JVM heap will crash.
The Solution: SXSSF (Streaming API)
For large datasets, use SXSSFWorkbook. It keeps only a small portion of the rows in memory (a sliding window) and flushes the rest to a temporary file on disk.
// Keep only 100 rows in memory, flush the rest to disk
SXSSFWorkbook wb = new SXSSFWorkbook(100);
// ... add thousands of rows ...
wb.dispose(); // Crucial: deletes temporary files
Formula Evaluation
If you write a formula into a cell using POI, the calculated value is not automatically generated until you open the file in Excel. If you need the value immediately within Java, use the FormulaEvaluator:
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateAll();
Summary
Apache POI is a powerful tool, but it requires careful resource management.
- Use XSSF for modern
.xlsxfiles. - Use Try-with-resources to ensure streams and workbooks are closed.
- Use SXSSF for large data exports to save memory.
- Always handle Cell Types when reading to avoid exceptions.