Java案例如何导入Excel?

wen python案例 2

本文目录导读:

Java案例如何导入Excel?

  1. 方案一:使用 Apache POI (最经典、最全面)
  2. 方案二:使用 EasyExcel (推荐,高性能、低内存)
  3. 总结与建议

在Java中导入Excel文件,常用的方案主要有三种:Apache POIEasyExcel(阿里开源的)和 JXL(已过时,不推荐)。

这里给你提供目前最主流的两种方案:Apache POI(功能全面,适合复杂处理)和 EasyExcel(性能优秀,适合大数据量,内存占用低)。


使用 Apache POI (最经典、最全面)

Apache POI 可以处理 .xls (HSSFWorkbook) 和 .xlsx (XSSFWorkbook) 格式。

添加 Maven 依赖

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.5</version> <!-- 使用较新版本 -->
</dependency>

核心代码示例(读取 .xlsx 文件)

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ImportExcelByPOI {
    public static List<List<String>> readExcel(String filePath) {
        List<List<String>> result = new ArrayList<>();
        try (InputStream is = new FileInputStream(filePath);
             Workbook workbook = new XSSFWorkbook(is)) { // 支持 .xlsx
            // 获取第一个工作表
            Sheet sheet = workbook.getSheetAt(0);
            if (sheet == null) {
                return result;
            }
            // 遍历每一行
            for (Row row : sheet) {
                // 跳过空行
                if (row == null) continue;
                List<String> rowData = new ArrayList<>();
                // 遍历单元格
                for (Cell cell : row) {
                    // 根据单元格类型获取数据
                    String cellValue = getCellValue(cell);
                    rowData.add(cellValue);
                }
                result.add(rowData);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    // 处理单元格数据类型的工具方法
    private static String getCellValue(Cell cell) {
        if (cell == null) return "";
        return switch (cell.getCellType()) {
            case STRING -> cell.getStringCellValue();
            case NUMERIC -> {
                // 如果是日期格式,处理为日期字符串,否则处理为数字
                if (DateUtil.isCellDateFormatted(cell)) {
                    yield cell.getLocalDateTimeCellValue().toString(); // 或者自定义格式化
                } else {
                    // 避免科学计数法,转为字符串
                    double value = cell.getNumericCellValue();
                    if (value == Math.floor(value) && !Double.isInfinite(value)) {
                        yield String.valueOf((long) value);
                    } else {
                        yield String.valueOf(value);
                    }
                }
            }
            case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
            case FORMULA -> cell.getCellFormula(); // 公式结果可以获取计算后的值
            case BLANK -> "";
            default -> "";
        };
    }
    public static void main(String[] args) {
        String filePath = "test.xlsx"; // 你的文件路径
        List<List<String>> data = readExcel(filePath);
        for (List<String> row : data) {
            System.out.println(row);
        }
    }
}

POI 的优缺点:

  • 优点:功能极其强大,能处理各种复杂的Excel格式(公式、样式、批注、图表等)。
  • 缺点:内存消耗较大,如果一次性读取一个5万行的大文件,可能会触发 OutOfMemoryError

使用 EasyExcel (推荐,高性能、低内存)

EasyExcel 是阿里巴巴开源的工具,对POI进行了封装,解决了大文件OOM问题,它是目前Java后端处理Excel的首选方案。

添加 Maven 依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>4.0.1</version> <!-- 使用较新版本 -->
</dependency>

核心代码示例(最简单的读取方式)

方式A:同步读取(小文件,一次性读进内存)

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import java.util.List;
public class ImportExcelByEasyExcel {
    public static void main(String[] args) {
        String fileName = "test.xlsx";
        // 同步读取:数据量不大时直接用
        List<Object> list = EasyExcel.read(fileName).sheet().doReadSync();
        for (Object obj : list) {
            // obj 默认是 List<String>,每一行的数据
            System.out.println(obj);
        }
    }
}

方式B:异步读取(推荐,支持大文件,逐行处理) 需要定义一个监听器类 DemoDataListener,继承 ReadListener<DemoData>

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
// 1. 定义数据模型(可选,但强烈推荐)
public class DemoData {
    private String name;
    private Integer age;
    private String email;
    // 必须要有 getter/setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}
// 2. 定义监听器
public class DemoDataListener implements ReadListener<DemoData> {
    private static final Logger log = LoggerFactory.getLogger(DemoDataListener.class);
    private static final int BATCH_COUNT = 100; // 每100条批量处理一次
    private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    private final DemoService demoService; // 可以注入业务Service
    public DemoDataListener() {
        this.demoService = new DemoService(); // 实际项目用 Spring 自动注入
    }
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        log.info("读取到一条数据:{}", data);
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止内存溢出
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData(); // 批量处理
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); // 清空列表
        }
    }
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 处理最后一批不满BATCH_COUNT的数据
        saveData();
        log.info("所有数据解析完成!");
    }
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", cachedDataList.size());
        demoService.saveBatch(cachedDataList); // 批量插入数据库
        log.info("存储数据库成功!");
    }
    // 如果第一行是表头,可以用这个方法来获取表头信息
    @Override
    public void invokeHead(Map<Integer, ReadCellHead> headMap, AnalysisContext context) {
        log.info("解析到表头:{}", headMap);
    }
}
// 3. 使用
public static void main(String[] args) {
    String fileName = "test.xlsx";
    // 核心调用:只需一行
    EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}

EasyExcel 的优缺点:

  • 优点
    • 内存占用极低(使用行缓存,不会一次性将整个文件读入内存)。
    • 使用简单,一行代码完成读取。
    • 支持模型映射(注解 @ExcelProperty)。
    • 性能好。
  • 缺点

    对格式的处理不如POI细致(如样式、图表等),但日常数据导入场景完全够用。


总结与建议

场景 推荐方案 原因
日常数据导入 (几千行以内,简单格式) EasyExcel 代码简洁,内存占用低。
大数据量导入 (几万行以上) EasyExcel (必须使用异步监听模式) POI会OOM,EasyExcel是唯一选择。
需要处理复杂的Excel格式 (公式、图表、透视表、样式) Apache POI 功能最底层、最全面。
需要处理 .xls (2003旧格式) POIEasyExcel 两者都支持,但POI支持更完善。

一句话总结:做Java后端导入Excel,首选 EasyExcel。

抱歉,评论功能暂时关闭!