JAVA解析EXCEL(JExcelAPI,POI,EasyExcel)

前言

文章目录

    • 前言
    • JExcelAPI
      • Demo
    • POI
      • HSSFWorkBook
      • XSSFWorkBook
        • Demo
      • SXSSFWorkBook
        • Demo
      • XSSFReader
        • Demo
    • EasyExcel
      • Demo

demo代码:https://github.com/RwTo/excel-demo
JAVA解析Excel 一般有三种方式

JExcelAPI
POI
EasyExcel

JExcelAPI

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

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

Demo

<!--JExcelAPI-->
		<dependency>
			<groupId>net.sourceforge.jexcelapi</groupId>
			<artifactId>jxl</artifactId>
			<version>2.6.12</version>
		</dependency>

读xls 文件

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 文件

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 文件

<!-- 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)

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)

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

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


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

<!--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>

实例

@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)

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, "alice@example.com"));
        dataList.add(new UserData("Bob", 30, "bob@example.com"));
        dataList.add(new UserData("Charlie", 28, "charlie@example.com"));

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

读取excel(xls,xlsx)

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());
            }
        }
    }
}

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2023年12月14日
下一篇 2023年12月14日

相关推荐