POI和 EasyExcel对Excel的简单操作

Java资料站

共 33251字,需浏览 67分钟

 ·

2021-04-22 13:30

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达

POI

Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。


结构

HSSF - 提供读写Microsoft Excel格式档案的功能。

XSSF - 提供读写Microsoft Excel OOXML格式档案的功能。

HWPF - 提供读写Microsoft Word格式档案的功能。

HSLF - 提供读写Microsoft PowerPoint格式档案的功能。

HDGF - 提供读写Microsoft Visio格式档案的功能。


应用

1.将用户信息等文件导出为excel表格(导出数据)

2.将用户信息等文件从excel中导入到数据库等(导入数据)


Excel

Excel中的对象

(1)工作簿:WorkBook

(2)工作表:Sheet

(3)行:Row

(4)列:Cell


excel2003版和excel2007版的区别

1、保存文档的格式不同

(1)Excel2003的保存格式为xxx.xls,其后缀名名为.xls。

(2)Excel2007的保存格式为xxx.xlsx,其后缀名名为.xlsx。

2、打开的文件类型不同

(1)Excel2003只能够打开后缀名为.xls的Excel文档,打开后缀名为.xlsx的Excel文档时,出现的是乱码。

(2)Excel2007不仅能够打开后缀名为.xls的Excel文档,也能打开后缀名为.xlsx的Excel文档。

3、可用的行和列不同

(1)Excel2003表格共有65536行,256列。

(2)Excel2007表格共有1048576行,16384列。


关于WorkBook

POI中的WorkBook接口下的三个实现类

HSSFWorkbook 是对03版本的excel文件操作的类

XSSFWorkbook 是对07版本的excel文件操作的类

SXSSFWorkbook Super XSSFWorkbook,是升级版的XSSFWorkbook,更快速的去对07版本的excel文件进行处理

下面我们将会对XSSFworkbook和SXSSFworkbook类对excel文件的操作进行时间上的对比。


POI的写入操作

1.HSSFWorkbook的写入操作

在maven中导入依赖

<!--  excel.xls(03版本的excel依赖)  -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
        </dependency>
<!--  excel.xlsx(07版本的excel依赖)  -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.9</version>
        </dependency>
<!--  日期格式化工具  -->
        <dependency>
            <groupId>org.wso2.orbit.joda-time</groupId>
            <artifactId>joda-time</artifactId>
            <version>2.9.4.wso2v1</version>
        </dependency>
<!--  测试工具  -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

设置一个全局变量PATH

String PATH = "C:\\Users\\26090\\IdeaProjects\\cn.pdsu.wbb\\src" ;

运行下面代码,生成一个名为 03版excel.xls 的excel文件

@Test
public void testWrite03() throws Exception {
    // 1.创建一个工作簿
    Workbook workbook = new HSSFWorkbook() ;
    // 2.创建一个工作表
    Sheet sheet = workbook.createSheet("POI初学者") ;
    // 3.创建一个行
    Row row1 = sheet.createRow(0) ;
    // 4.创建一个单元格(这里以行坐标和列坐标创建了一个单元格(0,0))
    Cell cell1 = row1.createCell(0) ;
    // 向单元格内写入数据
    cell1.setCellValue("姓名");
    // 创建单元格(0,1)
    Cell cell2 = row1.createCell(1) ;
    cell2.setCellValue("张三");

    // 创建行
    Row row2 = sheet.createRow(1) ;
    Cell cell3 = row2.createCell(0);
    cell3.setCellValue("时间");
    Cell cell4 = row2.createCell(1);
    // 创建日期并格式化
    String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
    cell4.setCellValue(time);

    // 将其生成一张表(使用IO流)    03版的excel文件的扩展名是xls
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "03版excel.xls") ;
    // 输出
    workbook.write(fileOutputStream);
    // 关闭流
    fileOutputStream.close();

    System.out.println("03版excel.xls文件生成完毕.");
}

代码执行后在src目录下生成的excel文件

打开文件可以看到其中的数据


2.XSSFWorkbook的写入操作

 @Test
public void testWrite07() throws Exception {
    // 1.创建一个工作簿
    Workbook workbook = new XSSFWorkbook() ;
    // 2.创建一个工作表
    Sheet sheet = workbook.createSheet("POI初学者") ;
    // 3.创建一个行
    Row row1 = sheet.createRow(0) ;
    // 4.创建一个单元格(这里以行坐标和列坐标创建了一个单元格(0,0))
    Cell cell1 = row1.createCell(0) ;
    // 向单元格内写入数据
    cell1.setCellValue("姓名");
    // 创建单元格(0,1)
    Cell cell2 = row1.createCell(1) ;
    cell2.setCellValue("张三");

    // 创建行
    Row row2 = sheet.createRow(1) ;
    Cell cell3 = row2.createCell(0);
    cell3.setCellValue("时间");
    Cell cell4 = row2.createCell(1);
    // 创建日期并格式化
    String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
    cell4.setCellValue(time);

    // 将其生成一张表(使用IO流)
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07版excel.xlsx") ;
    // 输出
    workbook.write(fileOutputStream);
    // 关闭流
    fileOutputStream.close();

    System.out.println("07版excel.xlsx文件生成完毕.");
}

代码执行后在src目录下生成的.xlsx文件


打开文件


操作较大数据量

在我们的日常应用中一般都是将数据批量的导入或导出的批量,下面我们就来看一下如何进行大数据量的导入


1.HSSF

缺点:关于03版的excel,因为其最多只有65536行,当我们导入的数据超过65536行时,它就会抛出异常

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


数据批量导入,导入65536行数据到03版的excel文件中

@Test
public void testWriteBigData03() throws Exception {
    // 开始时间
    long begin = System.currentTimeMillis() ;
    // 1.创建一个工作簿
    Workbook workbook = new HSSFWorkbook() ;
    // 2.创建一个工作表
    Sheet sheet = workbook.createSheet("POI初学者") ;

    // 写入数据
    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);     // 设置值
        }
    }
    // 将其生成一张表(使用IO流)
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "03版bigData_excel.xls") ;
    // 输出
    workbook.write(fileOutputStream);
    // 关闭流
    fileOutputStream.close();
    long end = System.currentTimeMillis() ;
    System.out.println((double) (end-begin)/1000);
    System.out.println("03版excel.xls文件生成完毕.");
}

可以看到耗时5.147s

而从生成的文件可以看到数据只到65536行,其下方没有空行

将生成的行数改为65537行时再次运行程序

此时的程序抛出了异常,我们在上面说过,03版的excel最多只能存储65536行数据,超过65536行就会异常。


2.XSSF

缺点:写入数据时数度较慢,非常的消耗内存,也有可能回发生内存溢出的情况

优点:可以写入较多的数据量


数据批量导入,导入65536行数据到07版的excel文件中

@Test
public void testWriteBigData07() throws Exception {
    // 开始时间
    long begin = System.currentTimeMillis() ;
    // 1.创建一个工作簿
    Workbook workbook = new XSSFWorkbook() ;
    // 2.创建一个工作表
    Sheet sheet = workbook.createSheet("POI初学者") ;

    // 写入数据
    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);     // 设置值
        }
    }
    // 将其生成一张表(使用IO流)
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07版bigData_excel.xlsx") ;
    // 输出
    workbook.write(fileOutputStream);
    // 关闭流
    fileOutputStream.close();
    long end = System.currentTimeMillis() ;
    System.out.println((double) (end-begin)/1000);
    System.out.println("07版excel.xlsx文件生成完毕.");
}

耗时10.972s,比起03版的耗时更长


可以看到在07版的excel文件中数据生成到6557行后其下方还是有空行


3.SXSSF

优点:可以写很大的数据量,切写数据的速度快,占用内存更少


在使用SXSSFWorkbook操作文件的过程中回生成临时文件,需要对临时文件进行清理

((SXSSFWorkbook)workbook).dispose() ;   // 删除临时文件

数据批量导入,使用SXSSFWorkbook类对象导入65536行数据到07版的excel文件中

@Test
public void testWriteBigDataS07() throws Exception {
    // 开始时间
    long begin = System.currentTimeMillis() ;
    // 1.创建一个工作簿
    Workbook workbook = new SXSSFWorkbook() ;
    // 2.创建一个工作表
    Sheet sheet = workbook.createSheet("POI初学者") ;

    // 写入数据
    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);     // 设置值
        }
    }
    // 将其生成一张表(使用IO流)
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "07版bigDataS_excel.xlsx") ;
    // 输出
    workbook.write(fileOutputStream);
    // 关闭流
    fileOutputStream.close();
    ((SXSSFWorkbook)workbook).dispose() ;   // 删除临时文件
    long end = System.currentTimeMillis() ;
    System.out.println((double) (end-begin)/1000);
    System.out.println("SXSSF_07版excel.xlsx文件生成完毕.");
}

处理相同的数据量耗时比之XSSFWorkbook要少

生成的excel文件


POI的读取操作

因为excel版本的不同所以对它的操作也分为两种


1.对03版本excel的操作

对于一个单元格数据的读取

@Test
public void testRead01() throws Exception {
    // 获取文件流
    FileInputStream inputStream = new FileInputStream(PATH + "src03版excel.xls") ;
    // 创建一个工作簿,用于操作excel的各种功能
    Workbook workbook = new HSSFWorkbook(inputStream) ;
    // 得到表
    Sheet sheet = workbook.getSheetAt(0) ;
    // 得到行
    Row row = sheet.getRow(0) ;
    // 得到列
    Cell cell = row.getCell(0) ;

    System.out.println(cell.getStringCellValue());
    inputStream.close();

}

在对excel文件中数据的获取时,我们需要针对不同的数据类型做出不同的获取方法


2.对07版本excel的操作

@Test
public void testRead02() throws Exception {
    // 获取文件流
    FileInputStream inputStream = new FileInputStream(PATH + "src07版excel.xlsx") ;
    // 创建一个工作簿,用于操作excel的各种功能
    Workbook workbook = new XSSFWorkbook(inputStream) ;
    // 得到表
    Sheet sheet = workbook.getSheetAt(0) ;
    // 得到行
    Row row = sheet.getRow(0) ;
    // 得到列
    Cell cell = row.getCell(0) ;

    System.out.println(cell.getStringCellValue());
    inputStream.close();
}

3.读取excel中不同的数据类型

先将表头数据读取出来,再将表中的数据进行类型的判断并输出

@Test
    public void testRead03() throws Exception {
        // 获取文件流
        FileInputStream inputStream = new FileInputStream(PATH + "src\\excel读操作.xlsx") ;
        // 创建一个工作簿,用于操作excel的各种功能
        Workbook workbook = new XSSFWorkbook(inputStream) ;
        Sheet sheet = workbook.getSheetAt(0) ;
        // 获取标题内容
        Row rowTitle = sheet.getRow(0) ;
        if(rowTitle!=null) {
            // 获取列数
            int cellCount = rowTitle.getPhysicalNumberOfCells();
            for(int cellNum = 0 ; cellNum < cellCount ; cellNum++) {
                Cell cell = rowTitle.getCell(cellNum) ;
                String cellValue = cell.getStringCellValue() ;
                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) {
                // 读取列
                int cellCount = rowTitle.getPhysicalNumberOfCells() ;
                for(int cellNum = 0 ;cellNum < cellCount ; cellNum++) {
                    Cell cell = rowData.getCell(cellNum) ;
                    // 匹配列的数据类型
                    if(cell!=null) {
                        CellType cellType = cell.getCellTypeEnum() ; // 获取数据的类型
                        String cellValue = "";

                        switch (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("【Number】");
                                if(HSSFDateUtil.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("【Error】");
                                break;
                        }
                        System.out.println(cellValue);
                    }
                }
            }
        }
        inputStream.close();
    }

在上面的操作中可以看到POI对03版和07版excel的操作的代码基本一致,只需修改文件的后缀和类名即可。


EasyExcel

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。


easyExcel官网:https://www.yuque.com/easyexcel


导入依赖

<!-- 导入easyExcel的pom依赖 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.7</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.76</version>
</dependency>

创建对象

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

    public void setString(String string) {
        this.string = string;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public void setDoubleData(Double doubleData) {
        this.doubleData = doubleData;
    }
}

自动生成数据

private List<DemoData> data() {
    List<DemoData> list = new ArrayList<DemoData>();
    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);
    }

写入数据

调用EasyExcel中的write()方法

其中write()中有两个参数,第一个参数是文件名,第二个参数是格式类

要想生成表要用到sheet()方法,sheet()方法中写入一个字符串表示表的名字

要想将数据写入表中还要调用doWrite()方法

格式如下:
EasyExcel.write(文件名, 类名.class).sheet(表明).doWrite(数据);

@Test
public void simpleWrite() {
    // 写法1
    String fileName = PATH + "easyTest.xlsx";
    // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
    // write的两个参数:文件名,格式类
    // sheet生成表明
    // doWrite写入数据
    EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}

执行后的结果


读取数据

假设这个是你的DAO存储

public class DemoDAO {
    public void save(List<DemoData> list) {
}

监听器

public class DemoDataListener extends AnalysisEventListener<DemoData> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
    /**
     * 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    List<DemoData> list = new ArrayList<DemoData>();
    /**
     * 假设这个是一个DAO
     */
    private DemoDAO demoDAO;
    public DemoDataListener() {
        demoDAO = new DemoDAO();
    }
    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }
    /**
     * 这个每一条数据解析都会来调用
     * DemoData 类型
     * AnalysisContext 分析上下文
     */
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        System.out.println(JSON.toJSONString(data));
        list.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list.clear();
        }
    }
    /**
     * 所有数据解析完成了 都会来调用
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        LOGGER.info("所有数据解析完成!");
    }
    /**
     * 加上存储数据库
     */
    private void saveData() {
        LOGGER.info("{}条数据,开始存储数据库!", list.size());
        demoDAO.save(list);
        LOGGER.info("存储数据库成功!");
    }
}

测试

@Test
public void simpleRead() {
    String fileName = PATH + "easyTest.xlsx";
    // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
    EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}

————————————————

版权声明:本文为CSDN博主「栗山未来~」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/weixin_45890113/article/details/115742939






粉丝福利:Java从入门到入土学习路线图

👇👇👇

👆长按上方微信二维码 2 秒


感谢点赞支持下哈 


浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报