JAVA解析EXCEL(JExcelAPI,POI,EasyExcel)

前言

文章目录

demo代码:https://github.com/RwTo/excel-demo

JAVA解析Excel 一般有三种方式

JExcelAPI

POI

EasyExcel

JExcelAPI

官网:https://jexcelapi.sourceforge.net/

  1. 仅支持 2003 版本的Excel 也就是 后缀名为 xls 的文件
  2. 采用流式处理模型,逐行读取和写入 ------因此 可以处理大量数据,一般不会出现OOM

Demo

xml 复制代码
<!--JExcelAPI-->
		<dependency>
			<groupId>net.sourceforge.jexcelapi</groupId>
			<artifactId>jxl</artifactId>
			<version>2.6.12</version>
		</dependency>

读xls 文件

java 复制代码
public class ReadExcelDemo {
    public static void main(String[] args) {
        try {
            // 1. 打开 Excel 文件
            String filePath = ExcelConstant.EXCEL_PATH_XLS;
            Workbook workbook = Workbook.getWorkbook(new java.io.File(filePath));

            // 2. 获取第一个工作表
            Sheet sheet = workbook.getSheet(0);
            /**
             * -Xms64m -Xmx64m
             * row = 4000 col = 50
             * 正常读
             */
            // 3. 遍历每一行,并读取数据
            for (int row = 0; row < sheet.getRows(); row++) {
                for (int col = 0; col < sheet.getColumns(); col++) {
                    Cell cell = sheet.getCell(col, row);
                    String content = cell.getContents();
                    content += cell.getCellFormat().getBackgroundColour().getDescription();
                    System.out.print(content+"\t");
                }
                System.out.println();
            }

            // 4. 关闭工作簿
            workbook.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

写xls 文件

java 复制代码
public class WriteExcelDemo {
    public static void main(String[] args) {
        try {
            String filePath = ExcelConstant.EXCEL_PATH_XLS;
            // 1. 创建工作簿
            WritableWorkbook workbook = Workbook.createWorkbook(new File(filePath));

            // 2. 创建工作表
            WritableSheet sheet = workbook.createSheet("Sheet1", 0);

            // 3. 定义单元格颜色
            WritableCellFormat greenFormat = new WritableCellFormat();
            greenFormat.setBackground(jxl.format.Colour.GREEN);

            WritableCellFormat yellowFormat = new WritableCellFormat();
            yellowFormat.setBackground(jxl.format.Colour.YELLOW);
            /**
             * -Xms64m -Xmx64m
             * row = 4000 col = 50
             * 正常写
             */
            // 4. 写入数据
            for (int row = 0; row < 4000; row++) {
                for (int col = 0; col < 50; col++) {
                    if(col%2 == 0){
                        Label label = new Label(col, row, "Cell " + (row + 1) + "-" + (col + 1),yellowFormat);
                        sheet.addCell(label);
                    }else{
                        Label label = new Label(col, row, "Cell " + (row + 1) + "-" + (col + 1),greenFormat);
                        sheet.addCell(label);
                    }
                }
            }

            // 5. 保存工作簿
            workbook.write();
            workbook.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

POI

官网:https://poi.apache.org/

功能比较丰富,使用较为广泛,问题也比较多(比如OOM)

四种API 操作Excel

官网介绍:https://poi.apache.org/components/spreadsheet/how-to.html

文档对象模型

HSSFWorkBook (功能丰富,但会OOM)

XSSFWorkBook (功能丰富,但易OOM)

事件处理模型(流式处理)

SXSSFWorkBook (只支持xlsx 的写入)

XSSFReader(仅提供模板,需要使用者自己编写)

POM 文件

xml 复制代码
<!-- Apache POI -->
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>4.1.2</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>4.1.2</version>
		</dependency>

HSSFWorkBook

  1. 支持 xls 文件的解析和写入
  2. 基于DOM ,将整个 Excel 文件加载到内存中,构建一个完整的 Excel 文档对象模型树,再进行解析和操作,当文件较大时,可能会出现内存溢出
    但因为 xls 文件支持的样式和单元格数量较少,一般不会出现OOM

XSSFWorkBook

  1. 支持 xlsx 文件的解析和写入
  2. 基于DOM ,将整个 Excel 文件加载到内存中,构建一个完整的 Excel 文档对象模型树,再进行解析和操作,当文件较大时,可能会出现内存溢出

Demo

写入excel(xls,xlsx)

java 复制代码
public class WriteExcelDemo {
    public static void main(String[] args) {
        try {

            String filePath = ExcelConstant.EXCEL_PATH_XLS;
            //String filePath = ExcelConstant.EXCEL_PATH_XLSX;
            //Workbook workbook = new XSSFWorkbook(); //支持xlsx文件的写入
            Workbook workbook = new HSSFWorkbook(); //支持xls文件的写入
            Sheet sheet = workbook.createSheet("Sheet1");
            /**
             * -Xms64m -Xmx64m
             * row = 4000 col = 50
             * 出现OOM
             */
            for (int row = 0; row < 4000; row++) {
                Row excelRow = sheet.createRow(row);
                for (int col = 0; col < 50; col++) {
                    Cell cell = excelRow.createCell(col);
                    cell.setCellValue("Cell " + (row + 1) + "-" + (col + 1));
                }
            }

            FileOutputStream fileOutputStream = new FileOutputStream(filePath);
            workbook.write(fileOutputStream);

            fileOutputStream.close();
            workbook.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

读excel(xls,xlsx)

java 复制代码
public class ReadExcelDemo {
    public static void main(String[] args) {
        try {
            String filePath = ExcelConstant.EXCEL_PATH_XLSX;
            //String filePath = ExcelConstant.EXCEL_PATH_XLSX;
            FileInputStream fileInputStream = new FileInputStream(filePath);
            //WorkbookFactory会根据文件类型自动选择使用HSSFWorkBook 或 XSSFWorkBook
            Workbook workbook = WorkbookFactory.create(fileInputStream);

            Sheet sheet = workbook.getSheetAt(0);
            /**
             * -Xms64m -Xmx64m
             * row = 4000 col = 50
             * 出现OOM
             */
            for (Row row : sheet) {
                for (Cell cell : row) {
                    String cellValue = getCellValueAsString(cell);
                    System.out.print(cellValue + "\t");
                }
                System.out.println();
            }

            fileInputStream.close();
            workbook.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static String getCellValueAsString(Cell cell) {
        if (cell == null) {
            return "";
        }

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                return String.valueOf(cell.getNumericCellValue());
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            default:
                return "";
        }
    }
}

SXSSFWorkBook

  1. 仅支持xlsx 的写入
  2. 基于流式处理,逐行对excel 处理,用磁盘空间换取内存,可以高效地写入数据到 Excel 文件中,同时减少内存占用。
  3. SXSSFWorkbook 采用了内存优化的设计,避免了将所有数据加载到内存的需求。它仅将有限数量的行缓冲在内存中,写入到输出流后即丢弃缓冲的数据,从而减少了内存占用。
  4. SXSSFWorkbook 支持设置在写入数据之前保留在内存中的行数。通过控制分页大小,可以灵活地控制内存使用,特别适合处理超大型的 Excel 文件。

Demo

写入xlsx

java 复制代码
public class SXSSFWriteExcelDemo {
    public static void main(String[] args) {
        try {
            // Set custom temporary directory
            String customTempDirPath = "temp";
            System.setProperty("java.io.tmpdir", customTempDirPath);

            String filePath = ExcelConstant.EXCEL_PATH_XLSX_BIG;
            SXSSFWorkbook workbook = new SXSSFWorkbook(500);//rowAccessWindowSize为内存中缓存的记录数,默认100
            Sheet sheet = workbook.createSheet("Sheet1");
            for (int row = 0; row < 100000; row++) {
                Row excelRow = sheet.createRow(row);
                for (int col = 0; col < 3; col++) {
                    Cell cell = excelRow.createCell(col);
                    cell.setCellValue("Cell " + (row + 1) + "-" + (col + 1));
                }
            }

            FileOutputStream fileOutputStream = new FileOutputStream(filePath);
            workbook.write(fileOutputStream);

            fileOutputStream.close();
            //使用 dispose() 方法释放(删除) SXSSFWorkbook 使用的临时资源,特别是在写入大量数据后,这一步骤很重要。
            workbook.dispose();
            /*如果不手动释放,默认等虚拟机停止也会删除临时文件
            poi.keep.tmp.files 通过这个配置可以控制虚拟机停止时,不删除临时文件*/

            //程序执行结束后,手动删除临时文件目录(看是否需要)
            deleteTempFiles(new File(customTempDirPath));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static void deleteTempFiles(File directory)  {
        if (directory.isDirectory()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    deleteTempFiles(file);
                }
            }
        }
        if (!directory.delete()) {
            System.err.println("Failed to delete temp file: " + directory.getAbsolutePath());
        } else {
            System.out.println("Deleted temp file: " + directory.getAbsolutePath());
        }
    }

}

XSSFReader

官网案例:https://poi.apache.org/components/spreadsheet/how-to.html#xssf_sax_api

  1. 仅支持 xlsx 的解析
  2. 基于流式处理,逐行读取单元格,不会将整个excel 存到内存,适合大型excel的处理

Demo

读xlsx

java 复制代码
public class SAXWriteExcelDemo {

    public static void main(String[] args) throws Exception {
        String filePath = ExcelConstant.EXCEL_PATH_XLSX_BIG;
        InputStream is = new FileInputStream(filePath);
        OPCPackage opcPackage = OPCPackage.open(is);
        try {
            // 读取 Excel 文件
            XSSFReader reader = new XSSFReader(opcPackage);
            // 使用事件处理器处理 Sheet 数据
            SAXSheetHandler sheetHandler = new SAXSheetHandler();
            XSSFSheetXMLHandler xmlHandler = new XSSFSheetXMLHandler(reader.getStylesTable(), reader.getSharedStringsTable(), sheetHandler, false);
            XMLReader sheetParser = XMLReaderFactory.createXMLReader();
            sheetParser.setContentHandler(xmlHandler);
            Iterator<InputStream> sheetsData = reader.getSheetsData();
            while(sheetsData.hasNext()){
                InputStream sheetIs = sheetsData.next();
                InputSource sheet = new InputSource(sheetIs);
                //开始解析sheet页
                System.out.println("=====================================开始处理sheet页==============================================");
                long start = System.currentTimeMillis();
                sheetParser.parse(sheet);
                System.out.println("=====================================处理sheet结束==============================================");
                long end = System.currentTimeMillis();
                List<List<SAXCell>> curSheet = sheetHandler.getCurSheet();
                curSheet.forEach(System.out::println);

                System.out.println("处理行数:"+sheetHandler.getRowCount());
                System.out.println("处理单元格数:"+sheetHandler.getCellCount());
                System.out.println("处理时间(ms):"+(end-start));

                sheetHandler.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (OpenXML4JException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        }finally {
            // 关闭输入流和 OPCPackage
            is.close();
            opcPackage.close();
        }
    }
}

class SAXSheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
    //当前页
    private List<List<SAXCell>> curSheet;
    //当前行
    private List<SAXCell> curRow;
    //总单元格数
    private int cellCount;

    public SAXSheetHandler() {
        this.curSheet = new LinkedList<>();
    }

    /**
     * 一行处理开始时
     *
     * @param i 行号
     */
    @Override
    public void startRow(int i) {
        //开始处理新的一行,初始化当前行
        curRow = new LinkedList<>();
    }

    /**
     * 一行处理结束时
     *
     * @param i 行号
     */
    @Override
    public void endRow(int i) {
        //一行处理结束,将这一行数据存入sheet
        curSheet.add(curRow);
    }


    /**
     * 处理单元格(不会读取空单元格)
     *
     * @param cellReference  单元格名称
     * @param formattedValue 单元格值
     * @param xssfComment    批注
     */
    @Override
    public void cell(String cellReference, String formattedValue, XSSFComment xssfComment) {
        SAXCell cell = new SAXCell(cellReference, formattedValue);
        curRow.add(cell);
        cellCount++;
    }

    @Override
    public void headerFooter(String text, boolean isHeader, String tagName) {
        // 头部和页脚
    }

    @Override
    public void endSheet() {
        //sheet处理结束
    }

    public List<List<SAXCell>> getCurSheet() {
        return curSheet;
    }

    public int getCellCount() {
        return cellCount;
    }

    public int getRowCount() {
        return curSheet.size();
    }

    public void clear(){
        curSheet = new LinkedList<>();
        cellCount = 0;
    }
}
@Data
class SAXCell {
    private String cellName;
    private String value;

    public SAXCell(String cellName, String value) {
        this.cellName = cellName;
        this.value = value;
    }
}

EasyExcel

官网:https://easyexcel.opensource.alibaba.com/

  1. 支持xlsx和xls 文件的解析和写入
  2. 流式处理,节省内存,可以处理大型excel
  3. 支持注解,简单易用

Demo

POM

xml 复制代码
<!--easyExcel-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>easyexcel</artifactId>
			<version>2.2.0-beta2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

实例

java 复制代码
@Data
public class UserData {
    private String name;
    private int age;
    private String email;

    public UserData() {
    }

    public UserData(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }
}

写入excel(xls,xlsx)

java 复制代码
public class EasyWriteExcelDemo {

    public static void main(String[] args) {
        String filePath = ExcelConstant.EE_EXCEL_PATH_XLSX_BIG;

        // 创建写入的数据列表
        List<UserData> dataList = new ArrayList<>();
        dataList.add(new UserData("Alice", 25, "[email protected]"));
        dataList.add(new UserData("Bob", 30, "[email protected]"));
        dataList.add(new UserData("Charlie", 28, "[email protected]"));

        // 使用 EasyExcel 写入 Excel 文件
        EasyExcel.write(filePath, UserData.class).sheet("Sheet1").doWrite(dataList);
    }
}

读取excel(xls,xlsx)

java 复制代码
public class EasyReadExcelDemo {

    public static void main(String[] args) {
        String filePath = ExcelConstant.EE_EXCEL_PATH_XLSX_BIG;

        // 使用 EasyExcel 读取 Excel 文件
        EasyExcel.read(filePath, UserData.class, new UserDataListener()).sheet().doRead();
    }

    public static class UserDataListener extends AnalysisEventListener<UserData> {
        private List<UserData> dataList = new ArrayList<>();

        @Override
        public void invoke(UserData data, AnalysisContext context) {
            dataList.add(data);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // 在这里可以对 dataList 中的数据进行处理,比如保存到数据库或其他操作
            for (UserData userData : dataList) {
                System.out.println(userData.getName() + "\t" + userData.getAge() + "\t" + userData.getEmail());
            }
        }
    }
}
相关推荐
xixixin_几秒前
【uniapp】uni.setClipboardData 方法失效 bug 解决方案
java·前端·uni-app
工业互联网专业5 分钟前
基于springboot+vue的校园二手物品交易平台
java·vue.js·spring boot·毕业设计·源码·课程设计·校园二手物品交易平台
isfox12 分钟前
一文拆解 Java CAS:从原理到避坑全攻略
java
JPC客栈19 分钟前
LeetCode面试经典 150 题(Java题解)
java·leetcode·面试
HyperAI超神经29 分钟前
【vLLM 学习】Aqlm 示例
java·开发语言·数据库·人工智能·学习·教程·vllm
异常驯兽师29 分钟前
IntelliJ IDEA 项目导入后 Java 文件图标显示为红色小写 j 的解决方法
java·路径配置
纪元A梦32 分钟前
华为OD机试真题——数据分类(2025A卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
java·javascript·c++·python·华为od·go·华为od机试题
小行星12533 分钟前
前端根据后端返回的excel二进制文件流进行导出下载
前端·excel
常年游走在bug的边缘41 分钟前
基于spring boot 集成 deepseek 流式输出 的vue3使用指南
java·spring boot·后端·ai
熙客1 小时前
Java并发:线程池
java