杰瑞科技汇

excelutils java

Of course! "ExcelUtils" in Java typically refers to a utility class—a helper class with static methods—that simplifies common operations for reading from and writing to Excel files. This is a very common and powerful pattern in enterprise applications.

Instead of reinventing the wheel, you would almost always use a well-established library like Apache POI to handle the complexities of the Excel file format (.xls, .xlsx).

Here’s a complete guide on what an ExcelUtils class is, why you'd use one, and how to build one using Apache POI.


The Core Library: Apache POI

Apache POI is the most popular Java library for manipulating Microsoft Office files. For Excel, it provides two main implementations:

  • HSSF (Horrible Spreadsheet Format): For older .xls files (Excel 97-2003).
  • XSSF (XML Spreadsheet Format): For modern .xlsx files (Excel 2007 and later).

You'll need to add the POI dependencies to your project.

Maven Dependencies (pom.xml)

<dependencies>
    <!-- For .xlsx files -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.5</version> <!-- Use the latest stable version -->
    </dependency>
    <!-- For .xls files (optional, but good to have) -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>5.2.5</version>
    </dependency>
    <!-- Required for some OOXML features -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-lite</artifactId>
        <version>5.2.5</version>
    </dependency>
</dependencies>

Why Create an ExcelUtils Class?

Directly using Apache POI can be verbose and repetitive. An ExcelUtils class provides a clean, simple, and reusable interface. Here are the benefits:

  • Simplicity: Hides complex POI logic behind simple method calls.
  • Reusability: Write the logic once and use it across your entire application.
  • Consistency: Ensures all Excel-related operations in your app follow the same pattern (e.g., same date format, same header styling).
  • Error Handling: Centralizes common error handling (e.g., IOException).
  • Readability: Your main business logic code becomes much cleaner.

Building a Practical ExcelUtils Class

Let's create a comprehensive ExcelUtils class with the most common operations:

  1. Write data from a List of Objects to an Excel file.
  2. Read data from an Excel file into a List of Objects.
  3. Write data from a 2D array (e.g., List<List<String>>).

The ExcelUtils.java Class

This class will be generic and use Java Reflection to map object fields to Excel columns.

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
public class ExcelUtils {
    private static final String[] DATE_FORMATS = {"MM/dd/yyyy", "dd-MM-yyyy", "yyyy-MM-dd"};
    /**
     * Writes a list of objects to an Excel file.
     * The headers of the Excel file will be the field names of the object.
     *
     * @param data        The list of objects to write.
     * @param filePath    The path to save the Excel file.
     * @param sheetName   The name of the sheet.
     * @throws IOException if an I/O error occurs.
     */
    public static <T> void writeDataToFile(List<T> data, String filePath, String sheetName) throws IOException {
        if (data == null || data.isEmpty()) {
            System.out.println("No data to write.");
            return;
        }
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet(sheetName);
            // Get the first object to determine headers
            T firstItem = data.get(0);
            Field[] fields = firstItem.getClass().getDeclaredFields();
            Row headerRow = sheet.createRow(0);
            // Create headers
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true); // Allow access to private fields
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(fields[i].getName());
            }
            // Create data rows
            int rowNum = 1;
            for (T item : data) {
                Row row = sheet.createRow(rowNum++);
                for (int i = 0; i < fields.length; i++) {
                    try {
                        Field field = fields[i];
                        Object value = field.get(item);
                        Cell cell = row.createCell(i);
                        setCellValue(cell, value);
                    } catch (IllegalAccessException e) {
                        System.err.println("Error accessing field: " + e.getMessage());
                    }
                }
            }
            // Auto-size columns for better readability
            for (int i = 0; i < fields.length; i++) {
                sheet.autoSizeColumn(i);
            }
            // Write the workbook to a file
            try (FileOutputStream fileOut = new FileOutputStream(filePath)) {
                workbook.write(fileOut);
            }
        }
        System.out.println("Excel file written successfully to: " + filePath);
    }
    /**
     * Reads data from an Excel file into a list of objects.
     *
     * @param filePath    The path to the Excel file.
     * @param sheetName   The name of the sheet to read.
     * @param clazz       The class of the objects to create.
     * @return A list of objects populated with data from the Excel file.
     * @throws IOException if an I/O error occurs.
     */
    public static <T> List<T> readDataFromFile(String filePath, String sheetName, Class<T> clazz) throws IOException {
        List<T> dataList = new ArrayList<>();
        try (InputStream inputStream = new FileInputStream(filePath);
             Workbook workbook = new XSSFWorkbook(inputStream)) {
            Sheet sheet = workbook.getSheet(sheetName);
            if (sheet == null) {
                throw new IllegalArgumentException("Sheet '" + sheetName + "' not found.");
            }
            // Get headers
            Row headerRow = sheet.getRow(0);
            Field[] fields = clazz.getDeclaredFields();
            int[] fieldIndices = new int[fields.length];
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                for (Cell cell : headerRow) {
                    if (fields[i].getName().equalsIgnoreCase(cell.getStringCellValue())) {
                        fieldIndices[i] = cell.getColumnIndex();
                        break;
                    }
                }
            }
            // Read data rows
            for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {
                Row row = sheet.getRow(rowNum);
                if (row == null) continue;
                try {
                    T instance = clazz.getDeclaredConstructor().newInstance();
                    for (int i = 0; i < fields.length; i++) {
                        int colIndex = fieldIndices[i];
                        if (colIndex != -1 && colIndex < row.getLastCellNum()) {
                            Cell cell = row.getCell(colIndex);
                            Object value = getCellValue(cell, fields[i].getType());
                            fields[i].set(instance, value);
                        }
                    }
                    dataList.add(instance);
                } catch (Exception e) {
                    System.err.println("Error processing row " + rowNum + ": " + e.getMessage());
                }
            }
        }
        return dataList;
    }
    // --- Helper Methods ---
    private static void setCellValue(Cell cell, Object value) {
        if (value == null) {
            cell.setCellValue("");
            return;
        }
        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 java.util.Date) {
            cell.setCellValue((java.util.Date) value);
            // You can apply a data format here if needed
            // CellStyle dateStyle = ...;
            // cell.setCellStyle(dateStyle);
        } else {
            cell.setCellValue(value.toString());
        }
    }
    private static Object getCellValue(Cell cell, Class<?> targetType) {
        if (cell == null) {
            return null;
        }
        CellType cellType = cell.getCellType();
        if (cellType == CellType.BLANK || cellType == CellType._NONE) {
            return null;
        }
        // Handle formula cells by evaluating them first
        if (cellType == CellType.FORMULA) {
            cellType = cell.getCachedFormulaResultType();
        }
        try {
            if (targetType == String.class) {
                return cell.getStringCellValue();
            } else if (targetType == Integer.class || targetType == int.class) {
                return (int) cell.getNumericCellValue();
            } else if (targetType == Long.class || targetType == long.class) {
                return (long) cell.getNumericCellValue();
            } else if (targetType == Double.class || targetType == double.class) {
                return cell.getNumericCellValue();
            } else if (targetType == Boolean.class || targetType == boolean.class) {
                return cell.getBooleanCellValue();
            } else if (targetType == java.util.Date.class) {
                // Dates are often stored as numeric values in Excel
                return cell.getDateCellValue();
            }
        } catch (Exception e) {
            System.err.println("Could not parse cell value: " + e.getMessage());
            return null;
        }
        return null;
    }
}

How to Use the ExcelUtils Class

First, let's define a simple data model.

Employee.java

public class Employee {
    private String name;
    private int id;
    private double salary;
    private java.util.Date joinDate;
    // No-arg constructor is required for reflection
    public Employee() {}
    // Getters and Setters
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public double getSalary() { return salary; }
    public void setSalary(double salary) { this.salary = salary; }
    public java.util.Date getJoinDate() { return joinDate; }
    public void setJoinDate(java.util.Date joinDate) { this.joinDate = joinDate; }
    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                ", joinDate=" + joinDate +
                '}';
    }
}

Main.java - Usage Example

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        // 1. Prepare data to write
        List<Employee> employeesToWrite = new ArrayList<>();
        employeesToWrite.add(new Employee("Alice", 101, 75000.50, new Date()));
        employeesToWrite.add(new Employee("Bob", 102, 82000.00, new Date()));
        employeesToWrite.add(new Employee("Charlie", 103, 68000.75, new Date()));
        String filePath = "employees.xlsx";
        String sheetName = "Employee Data";
        try {
            // 2. Write data to Excel file
            System.out.println("Writing data to Excel...");
            ExcelUtils.writeDataToFile(employeesToWrite, filePath, sheetName);
            // 3. Read data back from Excel file
            System.out.println("\nReading data from Excel...");
            List<Employee> employeesRead = ExcelUtils.readDataFromFile(filePath, sheetName, Employee.class);
            // 4. Print the read data to verify
            System.out.println("\nData read from Excel:");
            for (Employee emp : employeesRead) {
                System.out.println(emp);
            }
        } catch (IOException e) {
            System.err.println("An error occurred while processing the Excel file: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Advanced Topics and Best Practices

  • Styling: You can extend ExcelUtils to accept CellStyle objects to format cells (e.g., bold headers, currency formats, conditional formatting).
  • Large Files: For very large Excel files (100k+ rows), SXSSFWorkbook (from Apache POI) is a streaming API that is much more memory-efficient. You would modify the writeDataToFile method to use SXSSFWorkbook instead of XSSFWorkbook.
  • Error Handling: The example includes basic error handling. In a production environment, you might want to create custom exceptions (e.g., ExcelWriteException, ExcelParseException) for more specific error reporting.
  • Data Validation: You can add data validation rules to cells (e.g., a dropdown list, number ranges) using POI's DataValidation API.
  • Performance: For high-performance needs, consider other libraries like EasyExcel by Alibaba, which is known for its extremely low memory footprint and high speed.
分享:
扫描分享到社交APP
上一篇
下一篇