POI及EasyExcel

本文介绍了Apache POI和EasyExcel在处理Excel时的应用场景,如数据导出和导入。POI是一个复杂的库,而EasyExcel则以简单易用著称。在POI中,涉及HSSFWorkbook和XSSFWorkbook等不同版本的API,用于读写Excel文件。EasyExcel在处理大型文件时占用内存较少。文章还涵盖了POI的读写操作以及EasyExcel的基本用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.谈谈 POI 和 easyExcel

2.POI-Excel写

3.POI-Excel读

4.EasyExcel操作


1.谈谈 POI 和 easyExcel

应用场景

  • 1.将用户信息导出为excel表格(导出数据....)
  • 2.将Excel表中的信息录入到网站数据库(习题上传....)

开发中经常会设计到excel的处理,如导出Excel,导入Excel到数据库中!

操作Excel目前比较流行的就是 Apache POI 和 阿里巴巴的 easyExcel !

Apache POI

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

比较复杂

Excel是03版本的,行数有限制65535行

Excel OOXML 是07版本的,行数没有限制

easyExcel

由于POI比较复杂,就衍生了一个框架easyExcel

easyExcel 官网地址:https://github.com/alibaba/easyexcel

EasyExcel 是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。

EasyExcel 能大大减少占用内存的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中, 而是从磁盘上一行行读取数据,逐个解析。

下图是 EasyExcel 和 POI 在解析Excel时的对比图。 

官方文档(旧):https://www.yuque.com/easyexcel/doc/easyexcel 

官方文档(新):https://easyexcel.opensource.alibaba.com/docs/current/

2.POI-Excel写

  • 1.建立一个空项目 shuangma-poi,并配置1.8的项目结构,然后创建普通Maven的Moudle jiang-poi
  • 2.引入pom依赖
    <dependencies>
        <!--xls(03)-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.2</version>
        </dependency>
        <!--xlsx(07)-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.2</version>
        </dependency>
        <!--日期格式化工具-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.12.1</version>
        </dependency>
        <!--test-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
    </dependencies>

03 | 07 版本的写,就是对象不同,方法一样的!

需要注意:2003 版本和 2007 版本存在兼容性的问题!03最多只有 65535 行!

工作簿、工作表、行、单元格 

03版本:HSSFWorkbook

07版本:HSSFWorkbook

07升级版(加快速度处理07版本):SHSSFWorkbook

03版本:

public class ExcelWriteTest {
    String path = "H:\\java全栈\\企业常用第三方技术\\POI\\shuangma-poi";

    @Test
    public void testWrite03() throws IOException {
        // 创建新的Excel 工作簿
        Workbook workbook = new HSSFWorkbook();
        // 在Excel工作簿中建一工作表,其名为缺省值 Sheet0
        //Sheet sheet = workbook.createSheet();
        // 如要新建一名为"会员登录统计"的工作表,其语句为:
        Sheet sheet = workbook.createSheet("雙馬粉丝统计表");
        // 创建行(row 1)
        Row row1 = sheet.createRow(0);
        // 创建单元格(col 1-1)
        Cell cell11 = row1.createCell(0);
        cell11.setCellValue("今日新增关注");
        // 创建单元格(col 1-2)
        Cell cell12 = row1.createCell(1);
        cell12.setCellValue(999);
        // 创建行(row 2)
        Row row2 = sheet.createRow(1);
        // 创建单元格(col 2-1)
        Cell cell21 = row2.createCell(0);
        cell21.setCellValue("统计时间");
        //创建单元格(第三列)
        Cell cell22 = row2.createCell(1);
        String dateTime = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell22.setCellValue(dateTime);
        // 新建一输出文件流(注意:要先创建文件夹)
        FileOutputStream out = new FileOutputStream(path + "雙馬粉丝统计表03.xls ");
        // 把相应的Excel 工作簿存盘
        workbook.write(out);
        // 操作结束,关闭文件
        out.close();
        System.out.println("文件生成成功");
    }
}

07版本:

    @Test
    public void testWrite07() throws IOException {
        // 创建新的Excel 工作簿
        Workbook workbook = new XSSFWorkbook();
        // 在Excel工作簿中建一工作表,其名为缺省值 Sheet0
        //Sheet sheet = workbook.createSheet();
        // 如要新建一名为"会员登录统计"的工作表,其语句为:
        Sheet sheet = workbook.createSheet("雙馬粉丝统计表");
        // 创建行(row 1)
        Row row1 = sheet.createRow(0);
        // 创建单元格(col 1-1)
        Cell cell11 = row1.createCell(0);
        cell11.setCellValue("今日新增关注");
        // 创建单元格(col 1-2)
        Cell cell12 = row1.createCell(1);
        cell12.setCellValue(999);
        // 创建行(row 2)
        Row row2 = sheet.createRow(1);
        // 创建单元格(col 2-1)
        Cell cell21 = row2.createCell(0);
        cell21.setCellValue("统计时间");
        //创建单元格(第三列)
        Cell cell22 = row2.createCell(1);
        String dateTime = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell22.setCellValue(dateTime);
        // 新建一输出文件流(注意:要先创建文件夹)  07版本使用xlsx结尾
        FileOutputStream out = new FileOutputStream(path + "雙馬粉丝统计表07.xlsx ");
        // 把相应的Excel 工作簿存盘
        workbook.write(out);
        // 操作结束,关闭文件
        out.close();
        System.out.println("文件生成成功");
    }

03和07的区别在于接口的实现不同,文件的后缀不同

大文件写HSSF

缺点:最多只能处理65536行,否则会抛出异常

java.lang.IllegalArgumentException: Invalid row number (65536) outside
allowable range (0..65535)

优点:过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快

    @Test
    public void testWrite03BigData() throws IOException {
        //记录开始时间
        long begin = System.currentTimeMillis();
        //创建一个HSSFWorkbook
        Workbook workbook = new HSSFWorkbook();
        //创建一个sheet
        Sheet sheet = workbook.createSheet();
        //xls文件最大支持65536行
        for (int rowNum = 0; rowNum < 65536; rowNum++) {
            //创建一个行
            Row row = sheet.createRow(rowNum);
            for (int cellNum = 0; cellNum < 10; cellNum++) {//创建单元格
                Cell cell = row.createCell(cellNum);
                cell.setCellValue(cellNum);
            }
        }
        System.out.println("done");
        FileOutputStream out = new FileOutputStream(path + "bigdata03.xls");
        workbook.write(out);
        // 操作结束,关闭文件
        out.close();
        //记录结束时间
        long end = System.currentTimeMillis();
        System.out.println((double) (end - begin) / 1000);

    }

大文件写XSSF

缺点:写数据时速度非常慢,非常耗内存,也会发生内存溢出,如100万条

优点:可以写较大的数据量,如20万条

    @Test
    public void testWrite07BigData() throws IOException {
        //记录开始时间
        long begin = System.currentTimeMillis();
        //创建一个XSSFWorkbook
        Workbook workbook = new XSSFWorkbook();
        //创建一个sheet
        Sheet sheet = workbook.createSheet();
        //xls文件最大支持65536行
        for (int rowNum = 0; rowNum < 65536; rowNum++) {
            //创建一个行
            Row row = sheet.createRow(rowNum);
            for (int cellNum = 0; cellNum < 10; cellNum++) {//创建单元格
                Cell cell = row.createCell(cellNum);
                cell.setCellValue(cellNum);
            }
        }
        System.out.println("done");
        FileOutputStream out = new FileOutputStream(path + "bigdata07.xlsx");
        workbook.write(out);
        // 操作结束,关闭文件
        out.close();
        //记录结束时间
        long end = System.currentTimeMillis();
        System.out.println((double) (end - begin) / 1000);

    }

大文件写SXSSF

优点:可以写非常大的数据量,如100万条甚至更多条,写数据速度快,占用更少的内存

注意:

过程中会产生临时文件,需要清理临时文件

默认由100条记录被保存在内存中,

如果超过这数量,则最前面的数据被写入临时文件 如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook ( 数量 )

    @Test
    public void testWrite07BigDataS() throws IOException {
        //记录开始时间
        long begin = System.currentTimeMillis();
        //创建一个SXSSFWorkbook
        SXSSFWorkbook workbook = new SXSSFWorkbook();
        //创建一个sheet
        Sheet sheet = workbook.createSheet();
        //xls文件最大支持65536行
        for (int rowNum = 0; rowNum < 65536; rowNum++) {
            //创建一个行
            Row row = sheet.createRow(rowNum);
            for (int cellNum = 0; cellNum < 10; cellNum++) {//创建单元格
                Cell cell = row.createCell(cellNum);
                cell.setCellValue(cellNum);
            }
        }
        System.out.println("done");
        FileOutputStream out = new FileOutputStream(path + "bigdata07s.xlsx");
        workbook.write(out);
        // 操作结束,关闭文件
        out.close();
        workbook.dispose();
        //记录结束时间
        long end = System.currentTimeMillis();
        System.out.println((double) (end - begin) / 1000);

    }

SXSSFWorkbook-来至官方的解释:实现“BigGridDemo”策略的流式XSSFWorkbook版本。这允许写入 非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。

请注意,仍然可能会消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注释......仍然只存 储在内存中,因此如果广泛使用,可能需要大量内存。

3.POI-Excel读

03|07

03版本:

public class ExcelReadTest {
    String path = "H:\\java全栈\\企业常用第三方技术\\POI\\shuangma-poi";
    @Test
    public void testRead03() throws Exception {
        InputStream is = new FileInputStream(path + "雙馬粉丝统计表03.xls");
        Workbook workbook = new HSSFWorkbook(is);
        Sheet sheet = workbook.getSheetAt(0);
        // 读取第一行第一列
        Row row = sheet.getRow(0);
        Cell cell = row.getCell(0);
        // 输出单元内容
        System.out.println(cell.getStringCellValue());
        // 操作结束,关闭文件
        is.close();
    }
}

07版本:

    @Test
    public void testRead07() throws Exception {
        InputStream is = new FileInputStream(path + "雙馬粉丝统计表07.xlsx");
        Workbook workbook = new XSSFWorkbook(is);
        Sheet sheet = workbook.getSheetAt(0);
        // 读取第一行第一列
        Row row = sheet.getRow(0);
        Cell cell = row.getCell(0);
        // 输出单元内容
        System.out.println(cell.getStringCellValue());
        // 操作结束,关闭文件
        is.close();
    }

这里03和07都要注意读取值的类型

读取不同的数据类型

创建一个07版本的Excel内容如下

@Test
    public void testCellType() throws Exception {
        InputStream is = new FileInputStream(path + "/明细表.xlsx");
        Workbook workbook = new XSSFWorkbook(is);//这里因为有些版本的电脑已经默认使用07版本的excel了,这里就只演示07的了
        Sheet sheet = workbook.getSheetAt(0);
        // 读取标题所有内容
        Row rowTitle = sheet.getRow(0);
        if (rowTitle != null) {// 行不为空
            // 读取这一行有多少列 getPhysicalNumberOfCells
            int cellCount = rowTitle.getPhysicalNumberOfCells();
            for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                Cell cell = rowTitle.getCell(cellNum);
                if (cell != null) {
                    //CellType cellType = cell.getCellType();//获取他的类型
                    String cellValue = cell.getStringCellValue();//这里是知道他是String,就直接使用String类型,后面会讲到不知道是什么类型的情况
                    System.out.print(cellValue + "|");
                }
            }
            System.out.println();
        }
        // 读取商品列表数据
        int rowCount = sheet.getPhysicalNumberOfRows();//读取这一列有多少行
        for (int rowNum = 1; rowNum < rowCount; rowNum++) {
            Row rowData = sheet.getRow(rowNum);
            if (rowData != null) {// 行不为空
                // 读取cell
                int cellCount = rowTitle.getPhysicalNumberOfCells();
                for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                    System.out.print("【" + (rowNum + 1) + "-" + (cellNum + 1) +
                            "】");
                    Cell cell = rowData.getCell(cellNum);//得到当前循环的列
                    if (cell != null) {
                        CellType cellType = cell.getCellType();//获取他的类型
                        //判断单元格数据类型
                        String cellValue = "";
                        switch (cellType) {
                            //通过cellType直接访问静态成员
                            case STRING://字符串
                                System.out.print("【STRING】");
                                cellValue = cell.getStringCellValue();
                                break;
                            case BOOLEAN://布尔
                                System.out.print("【BOOLEAN】");
                                cellValue =
                                        String.valueOf(cell.getBooleanCellValue());
                                break;
                            case BLANK://空
                                System.out.print("【BLANK】");
                                break;
                            case NUMERIC:
                                System.out.print("【NUMERIC】");
                                //cellValue =
                                String.valueOf(cell.getNumericCellValue());
                                if (DateUtil.isCellDateFormatted(cell)) {//日期
                                    System.out.print("【日期】");
                                    Date date = cell.getDateCellValue();
                                    cellValue = new
                                            DateTime(date).toString("yyyy-MM-dd");
                                } else {
                                    // 不是日期格式,则防止当数字过长时以科学计数法显示
                                    System.out.print("【转换成字符串】");
                                    cell.setCellType(CellType.STRING);
                                    cellValue = cell.toString();
                                }
                                break;
                            case ERROR:
                                System.out.print("【数据类型错误】");
                                break;
                        }
                        System.out.println(cellValue);
                    }
                }
            }
        }
        is.close();
    }

计算公式

创建一个07版本的Excel内容如下

    @Test
    public void testFormula() throws Exception {
        InputStream is = new FileInputStream(path + "/计算公式.xlsx");
        XSSFWorkbook workbook = new XSSFWorkbook(is);
        Sheet sheet = workbook.getSheetAt(0);
        // 读取第五行第一列
        Row row = sheet.getRow(4);
        Cell cell = row.getCell(0);
        //公式计算器
        FormulaEvaluator formulaEvaluator = new
                XSSFFormulaEvaluator(workbook);//XSSFFormulaEvaluator公式计算器
        // 输出单元内容
        CellType cellType = cell.getCellType();
        switch (cellType) {
            case FORMULA://2
                //得到公式
                String formula = cell.getCellFormula();
                System.out.println(formula);
                CellValue evaluate = formulaEvaluator.evaluate(cell);//计算
                //String cellValue = String.valueOf(evaluate.getNumberValue());
                String cellValue = evaluate.formatAsString();//把计算结果格式化成一个字符串
                System.out.println(cellValue);
                break;
        }
    }

4.EasyExcel操作

创建一个新的模块jiang-EasyExcel

导入依赖

    <dependencies>
        <!--日期格式化工具-->
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.12.1</version>
        </dependency>
        <!--test-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

写入测试

写实体类

@Data
@EqualsAndHashCode
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}

写操作

public class TestEasyExcel {
    String path = "H:\\java全栈\\企业常用第三方技术\\POI\\shuangma-poi";
    //官网的通用数据
    private List<DemoData> data() {
        List<DemoData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }
    //根据list写入excel
    /**
     * 最简单的写
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>
     * 2. 直接写即可
     */
    @Test
    public void simpleWrite() {
        // 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入

        // 写法1 JDK8+
        // since: 3.0.0-beta1
        String fileName = path+"/xie.TestEasyExcel.xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        //doWrite(写数据)
        EasyExcel.write(fileName, DemoData.class)
                .sheet("模板")
                .doWrite(() -> {
                    // 分页查询数据
                    return data();
                });

        // 写法2
        fileName = path+"/TestEasyExcel2.xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());

        // 写法3
        fileName = path+"/TestEasyExcel3.xlsx";
        // 这里 需要指定写用哪个class去写
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(data(), writeSheet);
        }
    }
}

https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值