poi和easyExcel基于Java操作Excel学习笔记

Posted 蜜桃婷婷酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poi和easyExcel基于Java操作Excel学习笔记相关的知识,希望对你有一定的参考价值。

1 学习前言

Excel和读写和文件的读写没有本质的区别,都是属于IO操作,我们使用原生的IO就能解决Excel的导入和导出,当然操作起来比较麻烦,性能也不高,这次我们就学习poi和easyExcel(它们都属于第三方工具)的方式去基于Java和mysql数据库导入导出我们的Excel的数据

poi:Apache

easyExcel:阿里巴巴开源的工具

2 使用常用场景

1 将大量的数据导入为excel表格

2 将excel表中的信息录入到数据库(比如习题上传)(也叫批量插入),大大减轻我们人工乃至网站的录入量

开发中经常会涉及到对Excel的处理,如导入Excel,导出Excel到数据库

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

3 Apache POI

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


Apache给我们提供的POI不仅可以操作excel格式,对于word,ppt,visio都是提供支持的,而且语法大概类似

HSSF是处理普通的excel的

XSSF是处理OOXML格式的excel的

值得注意点是,虽然HSSF和XSSF都可以处理excel,但是还是有差距

比如excel主要有两个版本 03版和07版,其中03版的行数最多只能存65535行,07版的是没有限制的

如果想要操作Word,那么使用HWPF

如果想要操作PPT,那么使用HSLF

如果想要操作Visio,那么使用HDGF

原生的POI使用起来比较麻烦,而且量大的时候会报OOM异常(out of memory的简称,称之为内存溢出),但是现在很多市面上的工具底层也是会使用封装POI的

4 easyExcel(阿里巴巴开源工具)

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

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

easyExcel是阿里巴巴开源的一个对于excel处理工具(框架)以使用简单,节省内存著称

easyExcel能大大减少内存占用的主要原因是在解析excel中没有将文件数据一次性全部加载到内存中,而是从磁盘上一行一行的读取,逐个解析
由于POI比较原生比较复杂,我们后研发出新的(工具)框架easyExcel,它是阿里巴巴退出的对于POI的后续产品,并且对POI做了升级和优化,不会因为excel数据量过大引起oom异常(内存溢出),使我们用起来更加的方便(读写Excel只需要一行代码!)

5 poi和easyExcel的不同

主要在于内存和操作过程

比如我们现在excel表有100W数据

1 poi是先把数据加载到内存,如果我们内存比较小,Java是会直接报OOM内存溢出异常,这时候就会有问题,但是easyExcel它就比较简单只能,就算读写100W行数据,会一行一行的读写,不会直接全部解析

2poi会一次性全部读取和返回execl表格的数据,但是easyExcel不会这样,它没有将文件数据一次性全部加载到内存中,而是从磁盘上一行一行的读取,逐个解析

poi和easyExcel的本质上就是时间和空间的转换,根据我们的需求自行选择即可

6 poi excel写

1 创建Java项目

2 pom引入依赖

<!--导入依赖jar包-->
<!--xls(03)-->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.9</version>
</dependency>

<!--xlsx(07)-->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.9</version>
</dependency>

<!--日期格式化工具-->
<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.10.1</version>
</dependency>

<!--单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

3 本地创建两个excel 分别是03版和07版

首先03版,它最多有65536行

但是07版的,是没有限制的

他们对应的后缀也是不一样的,03版本的xls,07版本的是xslx,这意味着操作它们的工具类也不相同.03版本用的是poi,07版用的则是poi-ooxml

4 Java的宗旨就是万物皆对象,我们也要把excel当成我们的一个对象去处理

1 工作薄
首先我们打开的excel就是一个大对象,也叫工作簿,它包括以下内容

2 工作表
每一个sheet也是我们excel对象的属性,也叫工作表,我们肯定是现有工作簿才会有工作表,而且会默认自带sheet,也可以根据我们的需要自行添加sheet工作表

3 行
excel中有很多行,每一行也是我们excel对象的属性(横的叫行,竖的叫列)

4 列
excel中有很多列,每一列也是我们excel对象的属性(横的叫行,竖的叫列)

5 单元格
一行一列有很多个单元格,每一个单元格也是我们excel对象的属性

创建我们的测试类,创建Workbook对象,按着ctrl点进去发现它是一个接口

点击箭头可以看到它的三个实现类



定义工作簿,工作表,行列,单元格,和我们手动创建excel是一样的操作,只不过是用代码来实现


03版本excel IO操作写的全部代码如下

package com.wyh.Test;

import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.joda.time.DateTime;
import org.junit.Test;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Date;

/**
 * @program: JavaExecl
 * @description: 写excel 03版本
 * @author: 魏一鹤
 * @createDate: 2021-12-12 10:42
 **/

public class ExcelWrite03 
    //全局路径,供我们操作使用方便
    static String path="D:\\\\Tools\\\\JavaWorkSpace\\\\JavaExecl\\\\";
    @Test
    public  void  ExcelWrite03() throws Exception 
        //1创建一个工作簿
        Workbook workbook=new HSSFWorkbook();
        //2创建一个工作表 工作簿里面包含工作表,所以创建工作表要通过工作簿创建
        //默认的工作表是没有名字的,需要我们手动赋值,和我们在excel中更改sheet工作表的名称是一样的 操作
        Sheet sheet=workbook.createSheet("用户表");
        //3创建行 行也是在我们的表中存在的,所以需要用到表来创建
        //默认从0开始 也就是第一行
        Row row1 = sheet.createRow(0);
        //创建单元格 第一行的第一个数据 用坐标表示为(1,1)
        Cell cell11 = row1.createCell(0);
        //创建单元格 第一行的第二个数据 用坐标表示为(1,2)
        Cell cell12 = row1.createCell(1);
        //给单元格赋值
        cell11.setCellValue("姓名");
        cell12.setCellValue("魏一鹤");
        //创建第二行
        Row row2=sheet.createRow(1);
        //创建第二行的第一列
        Cell cell21 = row2.createCell(0);
        Cell cell22 = row2.createCell(1);
        //给单元格赋值(2.1)
        cell21.setCellValue("出生日期");
        //创建时间并且格式化
        String s = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        //给单元格赋值(2.2)
        cell22.setCellValue(s);
        //生成一张表 其实就是IO流操作 03版本就是使用xls文件结尾
        FileOutputStream fileOutputStream = new FileOutputStream(path + "用户测试03.xls");
        //输出工作簿
        workbook.write(fileOutputStream);
        //关闭流
        fileOutputStream.close();
        System.out.println("用户测试03.xls生成完毕");
    

运行后发现,会在项目本地生成我们定义的excel,打开查看



03版和07版的区别如下

1 03版本有最大长度现在 07版本没有

2 03版本后缀xls 07版本后缀xlsx

3 03版本使用的工具是HSSF,07版本使用的是XSSF

5 大数据绕导入导出(批量)

真实开发中,大多数就是大数据批量导入或者导出excel

大文件写HSSF

缺点:最多只能处理65536行,否则会报内存溢出异常

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

大文件写XSSF

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

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

03版本HSSF循环导入65536行数据(03版本最大行就是65536)

03版本HSSF循环插入65536条

package com.wyh.Test;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.junit.Test;

import java.io.FileOutputStream;

/**
 * @program: JavaExecl
 * @description: 大数据量写03版本
 * @author: 魏一鹤
 * @createDate: 2021-12-14 23:31
 **/

public class BigDateExcelWrite03 
    //全局路径,供我们操作使用方便
    static String path = "D:\\\\Tools\\\\JavaWorkSpace\\\\JavaExecl\\\\";

    @Test
    public void BigDateExcelWrite03() throws Exception 
        //开始时间 用于计算时间差
        long beginTime = System.currentTimeMillis();
        //创建工作簿  03版本使用HSSF
        Workbook workbook = new HSSFWorkbook();
        //创建工作表 这里就不给它命令了 按照默认的来
        Sheet sheet = workbook.createSheet();
        //写入数据 循环插入65536行数据,03版的HSSF最多只能插入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("生成excel表完毕");
        //03版本的后缀是xls
        //开启文件流
        FileOutputStream fileOutputStream = new FileOutputStream(path + "BigDateExcelWrite03.xlsx");
        //开始写excel
        workbook.write(fileOutputStream);
        //关闭流
        fileOutputStream.close();
        //结束时间
        long endTime = System.currentTimeMillis();
        //输出花费的时间
        System.out.println("花费的时间:"+(double)(endTime - beginTime)/1000);
    

运行发现excel已经创建成功,速度也非常的快

打开查看
已知03版本xls最多存65536行,那么如果我们循环插入65537行会怎么样呢? 保留源代码,循环最大值设置为65537

再次运行发现会报错

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

07版本XSSF循环插入65536条

把后缀改为xlsx,把HSSF缓存XSSF即可

package com.wyh.Test;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;

import java.io.FileOutputStream;

/**
 * @program: JavaExecl
 * @description: 大数据量写03版本
 * @author: 魏一鹤
 * @createDate: 2021-12-14 23:31
 **/

public class BigDateExcelWrite07 
    //全局路径,供我们操作使用方便
    static String path = "D:\\\\Tools\\\\JavaWorkSpace\\\\JavaExecl\\\\";

    @Test
    public void BigDateExcelWrite07() throws Exception 
        //开始时间 用于计算时间差
        long beginTime = System.currentTimeMillis();
        //创建工作簿 07版本的使用XSSF
        Workbook workbook = new XSSFWorkbook();
        //创建工作表 这里就不给它命令了 按照默认的来
        Sheet sheet = workbook.createSheet();
        //写入数据 循环插入65536行数据,03版的HSSF最多只能插入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("生成excel表完毕");
        //037版本的后缀是xlsx
        //开启文件流
        FileOutputStream fileOutputStream = new FileOutputStream(path + "BigDateExcelWrite07.xlsx");
        //开始写excel
        workbook.write(fileOutputStream);
        //关闭流
        fileOutputStream.close();
        //结束时间
        long endTime = System.currentTimeMillis();
        //输出花费的时间
        System.out.println("花费的时间:"+(double)(endTime - beginTime)/1000);
    

虽然也运行成功,但是可以明显感觉到速度不如03版HSSF,但是可以存更多的数据

打开excel查看发现数据到了65536停并没有结束,说明07版本XSSF上限不是65536,是可以存储更多的,可以写更多的数据

如果我们正在查看同一个文件,但是又进行其他操作,就会出现以下错误,我们把我们正在查看的文件关闭让它运行,等运行结束后再次打开即可

java.io.FileNotFoundException: D:\\Tools\\JavaWorkSpace\\JavaExecl\\BigDateExcelWrite07.xlsx (另一个程序正在使用此文件,进程无法访问。)

07版本XSSF导入100000条数据,把循环数改为100000即可


既然XSSF可以存这么多数据,但是速度比较慢,有没有方法可以优化效率呢(缓存,这个问题也可以叫做如何给poi加速

它就是Workbook接口三个实现类之一的SXSSFWorkbook,其他的两个我们上面都有操作过


这时候需要用到我们的SXSSF

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

注意
1 过程中会产生临时文件,需要清理临时文件
2 默认由100条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件,当然缓存数量也可以自定义
3 如果自定义内存中数据的数量,可以使用new SXSSFWorkbook(数量)


SXSSF循环插入100000条数据

package com.wyh.Test;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;

import java.io.FileOutputStream;

/**
 * @program: JavaExecl
 * @description: 大数据量写03版本
 * @author: 魏一鹤
 * @createDate: 2021-12-14 23:31
 **/

public class BigDateExcelWrite07Super 
    //全局路径,供我们操作使用方便
    static String path = "D:\\\\Tools\\\\JavaWorkSpace\\\\JavaExecl\\\\";

    @Test
    public void BigDateExcelWrite07Super() throws Exception 
        //开始时间 用于计算时间差
        long beginTime = System.currentTimeMillis();
        //创建工作簿 07版本的使用XSSF
        Workbook workbook = new SXSSFWorkbook();
        //创建工作表 这里就不给它命令了 按照默认的来
        Sheet sheet = workbook.createSheet();
        //写入数据 循环插入65536行数据,03版的HSSF最多只能插入65536行
        for (int rowNum = 0; rowNum < 100000; rowNum++) 
            //循环创建行
            Row row = sheet.createRow(rowNum);
            for(int cellNum=0;cellNum<10;cellNum++)
                //循环插入列
                Cell cell = row.createCell(cellNum);
                //循环设置值
                cell.setCellValue(cellNum);
            
        
        System.out.println("生成excel表完毕");
        //037版本的后缀是xlsx
        //开启文件流
        FileOutputStream fileOutputStream = new FileOutputStream(path + "BigDateExcelWrite07Super.xlsx");
        //开始写excel
        workbook.write(fileOutputStream);
        //关闭流
        fileOutputStream.close();
        //由于SXSSF会产生临时文件,这里我们需要清除下临时文件
        ((SXSSFWorkbook) workbook).dispose();
        //结束时间
        long endTime = System.currentTimeMillis();
        //输出花费的时间
        System.out.println("花费的时间:"+(double)(endTime - beginTime)/1000);
    
以上是关于poi和easyExcel基于Java操作Excel学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

Java简单使用EasyExcel操作读写excel

POI和EasyExcel

SpringBoot基于EasyExcel解析Excel实现文件导出导入读取写入

POI和easyExcel

POI和easyExcel

还在用 POI?试试 EasyExcel,轻松导出 100W 数据,不卡死,好用到爆!