Excel Export with Apache POI in Java

Apache POI is a powerful Java library for working with Microsoft Office documents. This comprehensive guide covers Excel export functionality including basic operations, styling, advanced features, and best practices.

Setup and Dependencies

1. Maven Dependencies

<dependencies>
<!-- Apache POI Core -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.4</version>
</dependency>
<!-- Apache POI OOXML for .xlsx files -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.4</version>
</dependency>
<!-- Optional: For styled exports -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.4</version>
</dependency>
</dependencies>

2. Basic Excel Export Service

@Service
public class ExcelExportService {
private static final Logger logger = LoggerFactory.getLogger(ExcelExportService.class);
/**
* Creates a basic workbook (XSSF for .xlsx, HSSF for .xls)
*/
public Workbook createWorkbook(boolean useXlsx) {
return useXlsx ? new XSSFWorkbook() : new HSSFWorkbook();
}
/**
* Basic data export to Excel
*/
public Workbook exportToExcel(List<List<Object>> data, String sheetName) {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet(sheetName);
int rowNum = 0;
for (List<Object> rowData : data) {
Row row = sheet.createRow(rowNum++);
int colNum = 0;
for (Object field : rowData) {
Cell cell = row.createCell(colNum++);
setCellValue(cell, field);
}
}
return workbook;
}
private void setCellValue(Cell cell, Object value) {
if (value == null) {
cell.setCellValue("");
} else if (value instanceof String) {
cell.setCellValue((String) value);
} else if (value instanceof Number) {
cell.setCellValue(((Number) value).doubleValue());
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
} else if (value instanceof Date) {
cell.setCellValue((Date) value);
} else if (value instanceof LocalDate) {
cell.setCellValue((LocalDate) value);
} else if (value instanceof LocalDateTime) {
cell.setCellValue((LocalDateTime) value);
} else {
cell.setCellValue(value.toString());
}
}
}

Advanced Excel Export with Styling

3. Comprehensive Excel Export with Styling

@Component
public class AdvancedExcelExportService {
private static final String DEFAULT_FONT_NAME = "Arial";
/**
* Export data with headers and styling
*/
public Workbook exportWithStyling(List<String> headers, 
List<List<Object>> data, 
String sheetName) {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet(sheetName);
// Create header style
CellStyle headerStyle = createHeaderStyle(workbook);
// Create data styles
CellStyle stringStyle = createStringStyle(workbook);
CellStyle numberStyle = createNumberStyle(workbook);
CellStyle dateStyle = createDateStyle(workbook);
CellStyle currencyStyle = createCurrencyStyle(workbook);
// Create header row
createHeaderRow(sheet, headers, headerStyle);
// Create data rows
createDataRows(sheet, data, stringStyle, numberStyle, dateStyle, currencyStyle);
// Auto-size columns
autoSizeColumns(sheet, headers.size());
return workbook;
}
private CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
// Background color
style.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// Font
Font font = workbook.createFont();
font.setFontName(DEFAULT_FONT_NAME);
font.setFontHeightInPoints((short) 11);
font.setBold(true);
font.setColor(IndexedColors.WHITE.getIndex());
style.setFont(font);
// Borders
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
// Alignment
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private CellStyle createStringStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName(DEFAULT_FONT_NAME);
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
return style;
}
private CellStyle createNumberStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName(DEFAULT_FONT_NAME);
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setAlignment(HorizontalAlignment.RIGHT);
// Number format
style.setDataFormat(workbook.createDataFormat().getFormat("#,##0.00"));
return style;
}
private CellStyle createDateStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName(DEFAULT_FONT_NAME);
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
// Date format
style.setDataFormat(workbook.createDataFormat().getFormat("yyyy-mm-dd"));
return style;
}
private CellStyle createCurrencyStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontName(DEFAULT_FONT_NAME);
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setBorderTop(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setAlignment(HorizontalAlignment.RIGHT);
// Currency format
style.setDataFormat(workbook.createDataFormat().getFormat("$#,##0.00"));
return style;
}
private void createHeaderRow(Sheet sheet, List<String> headers, CellStyle headerStyle) {
Row headerRow = sheet.createRow(0);
for (int i = 0; i < headers.size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(headers.get(i));
cell.setCellStyle(headerStyle);
}
}
private void createDataRows(Sheet sheet, List<List<Object>> data, 
CellStyle stringStyle, CellStyle numberStyle,
CellStyle dateStyle, CellStyle currencyStyle) {
int rowNum = 1;
for (List<Object> rowData : data) {
Row row = sheet.createRow(rowNum++);
for (int colNum = 0; colNum < rowData.size(); colNum++) {
Cell cell = row.createCell(colNum);
Object value = rowData.get(colNum);
setCellValueWithStyle(cell, value, stringStyle, numberStyle, dateStyle, currencyStyle);
}
}
}
private void setCellValueWithStyle(Cell cell, Object value, 
CellStyle stringStyle, CellStyle numberStyle,
CellStyle dateStyle, CellStyle currencyStyle) {
if (value == null) {
cell.setCellValue("");
cell.setCellStyle(stringStyle);
} else if (value instanceof String) {
cell.setCellValue((String) value);
cell.setCellStyle(stringStyle);
} else if (value instanceof Integer || value instanceof Long) {
cell.setCellValue(((Number) value).doubleValue());
cell.setCellStyle(numberStyle);
} else if (value instanceof Double || value instanceof Float) {
cell.setCellValue(((Number) value).doubleValue());
// Use currency style for monetary values
if (isMonetaryValue(value)) {
cell.setCellStyle(currencyStyle);
} else {
cell.setCellStyle(numberStyle);
}
} else if (value instanceof Boolean) {
cell.setCellValue((Boolean) value);
cell.setCellStyle(stringStyle);
} else if (value instanceof Date) {
cell.setCellValue((Date) value);
cell.setCellStyle(dateStyle);
} else if (value instanceof LocalDate) {
cell.setCellValue((LocalDate) value);
cell.setCellStyle(dateStyle);
} else if (value instanceof LocalDateTime) {
cell.setCellValue((LocalDateTime) value);
cell.setCellStyle(dateStyle);
} else {
cell.setCellValue(value.toString());
cell.setCellStyle(stringStyle);
}
}
private boolean isMonetaryValue(Object value) {
// Custom logic to determine if value represents money
return value instanceof Double && ((Double) value) != 0.0;
}
private void autoSizeColumns(Sheet sheet, int columnCount) {
for (int i = 0; i < columnCount; i++) {
sheet.autoSizeColumn(i);
}
}
}

Spring Controller for Excel Export

4. REST Controller for Excel Downloads

@RestController
@RequestMapping("/api/excel")
public class ExcelExportController {
@Autowired
private AdvancedExcelExportService excelExportService;
@Autowired
private UserService userService;
@Autowired
private ProductService productService;
/**
* Export users to Excel
*/
@GetMapping("/export-users")
public ResponseEntity<byte[]> exportUsersToExcel() {
try {
List<User> users = userService.getAllUsers();
List<String> headers = Arrays.asList(
"ID", "Username", "Email", "First Name", "Last Name", 
"Role", "Created Date", "Active"
);
List<List<Object>> data = convertUsersToData(users);
Workbook workbook = excelExportService.exportWithStyling(headers, data, "Users");
return createExcelResponse(workbook, "users_export.xlsx");
} catch (Exception e) {
logger.error("Error exporting users to Excel", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* Export products to Excel with filtering
*/
@GetMapping("/export-products")
public ResponseEntity<byte[]> exportProductsToExcel(
@RequestParam(required = false) String category,
@RequestParam(required = false) Boolean inStock) {
try {
List<Product> products = productService.getProducts(category, inStock);
List<String> headers = Arrays.asList(
"Product ID", "Name", "Category", "Price", "Stock Quantity",
"Description", "Created Date", "Active"
);
List<List<Object>> data = convertProductsToData(products);
Workbook workbook = excelExportService.exportWithStyling(headers, data, "Products");
String filename = "products_export.xlsx";
if (category != null) {
filename = "products_" + category + "_export.xlsx";
}
return createExcelResponse(workbook, filename);
} catch (Exception e) {
logger.error("Error exporting products to Excel", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* Custom export with request body
*/
@PostMapping("/custom-export")
public ResponseEntity<byte[]> customExport(@RequestBody ExportRequest request) {
try {
// Process export based on request
List<String> headers = request.getHeaders();
List<List<Object>> data = request.getData();
Workbook workbook = excelExportService.exportWithStyling(
headers, data, request.getSheetName());
return createExcelResponse(workbook, request.getFileName());
} catch (Exception e) {
logger.error("Error in custom export", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
private List<List<Object>> convertUsersToData(List<User> users) {
List<List<Object>> data = new ArrayList<>();
for (User user : users) {
List<Object> row = new ArrayList<>();
row.add(user.getId());
row.add(user.getUsername());
row.add(user.getEmail());
row.add(user.getFirstName());
row.add(user.getLastName());
row.add(user.getRole());
row.add(user.getCreatedDate());
row.add(user.isActive());
data.add(row);
}
return data;
}
private List<List<Object>> convertProductsToData(List<Product> products) {
List<List<Object>> data = new ArrayList<>();
for (Product product : products) {
List<Object> row = new ArrayList<>();
row.add(product.getId());
row.add(product.getName());
row.add(product.getCategory());
row.add(product.getPrice());
row.add(product.getStockQuantity());
row.add(product.getDescription());
row.add(product.getCreatedDate());
row.add(product.isActive());
data.add(row);
}
return data;
}
private ResponseEntity<byte[]> createExcelResponse(Workbook workbook, String filename) 
throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
workbook.close();
byte[] bytes = outputStream.toByteArray();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", filename);
headers.setContentLength(bytes.length);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
}
// DTO for custom export requests
class ExportRequest {
private List<String> headers;
private List<List<Object>> data;
private String sheetName;
private String fileName;
// Constructors, getters, and setters
public ExportRequest() {}
public ExportRequest(List<String> headers, List<List<Object>> data, 
String sheetName, String fileName) {
this.headers = headers;
this.data = data;
this.sheetName = sheetName;
this.fileName = fileName;
}
public List<String> getHeaders() { return headers; }
public void setHeaders(List<String> headers) { this.headers = headers; }
public List<List<Object>> getData() { return data; }
public void setData(List<List<Object>> data) { this.data = data; }
public String getSheetName() { return sheetName; }
public void setSheetName(String sheetName) { this.sheetName = sheetName; }
public String getFileName() { return fileName; }
public void setFileName(String fileName) { this.fileName = fileName; }
}

Advanced Features

5. Excel Export with Multiple Sheets

@Component
public class MultiSheetExcelExportService {
/**
* Export multiple datasets to different sheets
*/
public Workbook exportMultipleSheets(Map<String, SheetData> sheetDataMap) {
Workbook workbook = new XSSFWorkbook();
for (Map.Entry<String, SheetData> entry : sheetDataMap.entrySet()) {
String sheetName = entry.getKey();
SheetData sheetData = entry.getValue();
createSheet(workbook, sheetName, sheetData);
}
return workbook;
}
/**
* Export with summary sheet
*/
public Workbook exportWithSummary(List<ReportData> reportDataList) {
Workbook workbook = new XSSFWorkbook();
// Create summary sheet
createSummarySheet(workbook, reportDataList);
// Create detailed sheets
for (ReportData reportData : reportDataList) {
createDetailedSheet(workbook, reportData);
}
return workbook;
}
private void createSheet(Workbook workbook, String sheetName, SheetData sheetData) {
Sheet sheet = workbook.createSheet(sheetName);
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
// Create header
Row headerRow = sheet.createRow(0);
for (int i = 0; i < sheetData.getHeaders().size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(sheetData.getHeaders().get(i));
cell.setCellStyle(headerStyle);
}
// Create data rows
int rowNum = 1;
for (List<Object> rowData : sheetData.getData()) {
Row row = sheet.createRow(rowNum++);
for (int colNum = 0; colNum < rowData.size(); colNum++) {
Cell cell = row.createCell(colNum);
setCellValue(cell, rowData.get(colNum));
cell.setCellStyle(dataStyle);
}
}
// Auto-size columns
for (int i = 0; i < sheetData.getHeaders().size(); i++) {
sheet.autoSizeColumn(i);
}
}
private void createSummarySheet(Workbook workbook, List<ReportData> reportDataList) {
Sheet sheet = workbook.createSheet("Summary");
CellStyle headerStyle = createHeaderStyle(workbook);
CellStyle dataStyle = createDataStyle(workbook);
// Create summary headers
String[] summaryHeaders = {"Report Name", "Total Records", "Generated Date"};
Row headerRow = sheet.createRow(0);
for (int i = 0; i < summaryHeaders.length; i++) {
Cell cell = headerRow.createCell(i);
cell.setCellValue(summaryHeaders[i]);
cell.setCellStyle(headerStyle);
}
// Create summary data
int rowNum = 1;
for (ReportData reportData : reportDataList) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(reportData.getName());
row.createCell(1).setCellValue(reportData.getRecordCount());
row.createCell(2).setCellValue(new Date());
}
// Auto-size columns
for (int i = 0; i < summaryHeaders.length; i++) {
sheet.autoSizeColumn(i);
}
}
private void createDetailedSheet(Workbook workbook, ReportData reportData) {
// Implementation for detailed sheets
Sheet sheet = workbook.createSheet(reportData.getName());
// Add detailed data...
}
private CellStyle createHeaderStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setBold(true);
style.setFont(font);
return style;
}
private CellStyle createDataStyle(Workbook workbook) {
return workbook.createCellStyle();
}
private void setCellValue(Cell cell, Object value) {
// Implementation as shown earlier
}
}
class SheetData {
private List<String> headers;
private List<List<Object>> data;
// Constructors, getters, setters
public SheetData(List<String> headers, List<List<Object>> data) {
this.headers = headers;
this.data = data;
}
public List<String> getHeaders() { return headers; }
public List<List<Object>> getData() { return data; }
}
class ReportData {
private String name;
private int recordCount;
private List<List<Object>> data;
// Constructors, getters, setters
}

6. Excel Export with Charts

@Component
public class ExcelChartExportService {
/**
* Export data with chart
*/
public Workbook exportWithChart(List<SalesData> salesData) {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet("Sales Report");
// Create data
createSalesData(sheet, salesData);
// Create chart
createChart(workbook, sheet, salesData.size());
return workbook;
}
private void createSalesData(Sheet sheet, List<SalesData> salesData) {
// Create headers
Row headerRow = sheet.createRow(0);
headerRow.createCell(0).setCellValue("Month");
headerRow.createCell(1).setCellValue("Sales");
headerRow.createCell(2).setCellValue("Target");
// Create data rows
int rowNum = 1;
for (SalesData data : salesData) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(data.getMonth());
row.createCell(1).setCellValue(data.getSales());
row.createCell(2).setCellValue(data.getTarget());
}
// Auto-size columns
for (int i = 0; i < 3; i++) {
sheet.autoSizeColumn(i);
}
}
private void createChart(Workbook workbook, Sheet sheet, int dataSize) {
Drawing<?> drawing = sheet.createDrawingPatriarch();
ClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 4, 0, 10, 15);
Chart chart = drawing.createChart(anchor);
ChartLegend legend = chart.getOrCreateLegend();
legend.setPosition(LegendPosition.BOTTOM);
LineChartData chartData = chart.getChartDataFactory().createLineChartData();
ChartAxis bottomAxis = chart.getChartAxisFactory().createCategoryAxis(AxisPosition.BOTTOM);
ValueAxis leftAxis = chart.getChartAxisFactory().createValueAxis(AxisPosition.LEFT);
leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
ChartDataSource<String> categoryDS = ChartDataSources.fromStringCellRange(
sheet, new CellRangeAddress(1, dataSize, 0, 0));
ChartDataSource<Number> salesDS = ChartDataSources.fromNumericCellRange(
sheet, new CellRangeAddress(1, dataSize, 1, 1));
ChartDataSource<Number> targetDS = ChartDataSources.fromNumericCellRange(
sheet, new CellRangeAddress(1, dataSize, 2, 2));
chartData.addSeries(categoryDS, salesDS).setTitle("Actual Sales");
chartData.addSeries(categoryDS, targetDS).setTitle("Sales Target");
chart.plot(chartData, bottomAxis, leftAxis);
}
}
class SalesData {
private String month;
private double sales;
private double target;
// Constructors, getters, setters
public SalesData(String month, double sales, double target) {
this.month = month;
this.sales = sales;
this.target = target;
}
public String getMonth() { return month; }
public double getSales() { return sales; }
public double getTarget() { return target; }
}

Performance Optimization

7. Large Dataset Export with Streaming

@Component
public class StreamingExcelExportService {
private static final int BATCH_SIZE = 1000;
/**
* Export large datasets using SXSSF for memory efficiency
*/
public Workbook exportLargeDataset(List<List<Object>> data, String sheetName) {
// Use SXSSF for streaming (keeps only a certain number of rows in memory)
Workbook workbook = new SXSSFWorkbook(100); // Keep 100 rows in memory
Sheet sheet = workbook.createSheet(sheetName);
int rowNum = 0;
for (List<Object> rowData : data) {
Row row = sheet.createRow(rowNum++);
int colNum = 0;
for (Object field : rowData) {
Cell cell = row.createCell(colNum++);
setCellValue(cell, field);
}
// Flush every BATCH_SIZE rows to manage memory
if (rowNum % BATCH_SIZE == 0) {
((SXSSFSheet) sheet).flushRows(BATCH_SIZE);
}
}
return workbook;
}
/**
* Export with progress tracking
*/
public Workbook exportWithProgress(List<List<Object>> data, 
String sheetName,
ProgressTracker progressTracker) {
Workbook workbook = new SXSSFWorkbook(100);
Sheet sheet = workbook.createSheet(sheetName);
int totalRows = data.size();
int rowNum = 0;
for (List<Object> rowData : data) {
Row row = sheet.createRow(rowNum++);
int colNum = 0;
for (Object field : rowData) {
Cell cell = row.createCell(colNum++);
setCellValue(cell, field);
}
// Update progress
if (progressTracker != null) {
int progress = (int) ((rowNum * 100.0) / totalRows);
progressTracker.updateProgress(progress);
}
if (rowNum % BATCH_SIZE == 0) {
((SXSSFSheet) sheet).flushRows(BATCH_SIZE);
}
}
return workbook;
}
private void setCellValue(Cell cell, Object value) {
// Implementation as shown earlier
}
}
interface ProgressTracker {
void updateProgress(int percentage);
}

Testing Excel Export

8. Unit Tests for Excel Export

@SpringBootTest
public class ExcelExportServiceTest {
@Autowired
private AdvancedExcelExportService excelExportService;
@Test
public void testExcelExport() throws IOException {
// Given
List<String> headers = Arrays.asList("Name", "Age", "Salary");
List<List<Object>> data = Arrays.asList(
Arrays.asList("John Doe", 30, 50000.0),
Arrays.asList("Jane Smith", 25, 60000.0)
);
// When
Workbook workbook = excelExportService.exportWithStyling(headers, data, "Test");
// Then
assertNotNull(workbook);
assertEquals(1, workbook.getNumberOfSheets());
Sheet sheet = workbook.getSheet("Test");
assertNotNull(sheet);
assertEquals(3, sheet.getRow(0).getPhysicalNumberOfCells()); // Header cells
assertEquals(2, sheet.getLastRowNum()); // Data rows (0-based header + 2 data rows)
workbook.close();
}
@Test
public void testLargeExcelExport() throws IOException {
// Given
List<List<Object>> largeData = generateLargeDataset(10000);
// When
Workbook workbook = excelExportService.exportToExcel(largeData, "LargeData");
// Then
assertNotNull(workbook);
Sheet sheet = workbook.getSheet("LargeData");
assertEquals(10000, sheet.getLastRowNum());
workbook.close();
}
private List<List<Object>> generateLargeDataset(int size) {
List<List<Object>> data = new ArrayList<>();
for (int i = 0; i < size; i++) {
data.add(Arrays.asList("User " + i, i, 1000.0 + i));
}
return data;
}
}

Best Practices and Error Handling

9. Error Handling and Utilities

@Component
public class ExcelExportUtils {
private static final Logger logger = LoggerFactory.getLogger(ExcelExportUtils.class);
/**
* Safely export with error handling
*/
public ResponseEntity<byte[]> safeExport(ExportFunction exportFunction, String filename) {
try {
Workbook workbook = exportFunction.export();
return convertToResponse(workbook, filename);
} catch (Exception e) {
logger.error("Error during Excel export: {}", filename, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(("Error generating Excel file: " + e.getMessage()).getBytes());
}
}
/**
* Validate export data
*/
public void validateExportData(List<String> headers, List<List<Object>> data) {
if (headers == null || headers.isEmpty()) {
throw new IllegalArgumentException("Headers cannot be null or empty");
}
if (data == null) {
throw new IllegalArgumentException("Data cannot be null");
}
for (int i = 0; i < data.size(); i++) {
List<Object> row = data.get(i);
if (row.size() != headers.size()) {
throw new IllegalArgumentException(
String.format("Row %d has %d columns, but headers have %d columns", 
i, row.size(), headers.size()));
}
}
}
/**
* Sanitize sheet name (Excel has restrictions on sheet names)
*/
public String sanitizeSheetName(String proposedName) {
if (proposedName == null) {
return "Sheet1";
}
// Remove invalid characters
String sanitized = proposedName.replaceAll("[\\\\/*\\[\\]:?]", "");
// Truncate if too long (Excel limit is 31 characters)
if (sanitized.length() > 31) {
sanitized = sanitized.substring(0, 31);
}
// Ensure not empty
if (sanitized.trim().isEmpty()) {
return "Sheet1";
}
return sanitized;
}
private ResponseEntity<byte[]> convertToResponse(Workbook workbook, String filename) 
throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
workbook.write(outputStream);
workbook.close();
byte[] bytes = outputStream.toByteArray();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDispositionFormData("attachment", filename);
headers.setContentLength(bytes.length);
return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
}
}
@FunctionalInterface
interface ExportFunction {
Workbook export() throws Exception;
}

Key Features Summary

  • Basic Excel Export: Simple data export to Excel format
  • Advanced Styling: Custom fonts, colors, borders, and number formatting
  • Multiple Sheets: Export multiple datasets to different sheets
  • Charts and Graphics: Create visual representations of data
  • Performance Optimization: Streaming for large datasets
  • Error Handling: Robust error handling and validation
  • REST API Integration: Spring Controller for web exports
  • Testing: Comprehensive unit tests

Apache POI provides extensive capabilities for Excel export in Java applications, making it suitable for everything from simple data exports to complex, styled reports with advanced features.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper