使用java读写Excel表格文档

使用java读写Excel表格文档

前言: 在我们实际开发中, 难免会遇到类似把数据库中的数据生成excel文档, 或者读取excel中的数据存入到数据库等需求, 所以我们才会学习POI, 阿里云基于POI封装了EasyExcel, 方便了开发人员的使用以及弥补一些POI的安全和性能问题

POI

POI是Apache软件基金会的,POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。
所以POI的主要功能是可以用Java操作Microsoft Office的相关文件,这里我们主要讲Excel

1. 写入

1.1 pom依赖

<!--2003(xls)-->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
</dependency>
<!--2007(xlsx)版本-->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>

所谓03版和07版最大的区别在于 03版表格最多支持65536行, 而07版则没有上限

1.2 基本功能

​ 结构

  • HSSF - 提供读写Microsoft Excel格式文档的功能 (03版)
  • XSSF - 提供读写Microsoft Excel OOXML格式文档的功能(07版)
  • HWPF - 提供读写Microsoft Word格式文档的功能(word文档)
  • HSLF - 提供读写Microsoft PowerPoint格式文档的功能(PPT幻灯片)
  • HDGF - 提供读写Microsoft Visio格式文档的功能

1.3 Workbook接口的三个实现类

实现类 描述
HSSFWorkbook() 03版写入
XSSFWorkbook() 07版写入 (把所有的数据保存到内存 ,再写入到文件.
可能发生内存泄露)
SXSSFWorkbook() 写入速度快, 占用更少的内存. 中间会产生一个临时文件,
需要清理临时文件, 默认由100条记录被保存到内存中,
如果超出这个范围,则被写入到临时文件中,如果想自
定义内存中的数量 ,可以 new SXSSFWorkbook(数量) 来进行定义

1.4 写入文件演示 (03版)

// 生成文件路径定义
String PATH = "/home/zpk/DEV/idea/workspace/poi/";

@Test
public void testWrite03() throws Exception {
    // 1. 创建一个工作簿
    Workbook workbook = new HSSFWorkbook();
    // 2. 创建一个工作表
    Sheet sheet = workbook.createSheet("统计表");

    // 3. 创建一个行 (第一行)
    Row row1 = sheet.createRow(0);
    // 创建单元格(1,1)
    Cell cell11 = row1.createCell(0);
    // 单元格(1,1)写入内容
    cell11.setCellValue("今日新增观众");
    // 创建单元格(1,2)
    Cell cell12 = row1.createCell(1);
    // 单元格(1,2)写入内容
    cell12.setCellValue(666);

    // 第二行
    Row row2 = sheet.createRow(1);
    Cell cell21 = row2.createCell(0);
    cell21.setCellValue("统计时间");
    Cell cell22 = row2.createCell(1);
    String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
    cell22.setCellValue(time);

    // 生成一张表(IO流) 03 版本就是使用xls结尾
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "统计表03版.xls");
    // 写出文件
    workbook.write(fileOutputStream);
    // 关闭流
    fileOutputStream.close();

    System.out.println("文件生成完毕");

}

1.5 写入文件演示 (07版)

// 生成文件路径定义
String PATH = "/home/zpk/DEV/idea/workspace/poi/";

@Test
public void testWrite07() throws Exception {
    // 1. 创建一个工作簿
    Workbook workbook = new XSSFWorkbook();
    // 2. 创建一个工作表
    Sheet sheet = workbook.createSheet("统计表");

    // 3. 创建一个行 (第一行)
    Row row1 = sheet.createRow(0);
    // 创建单元格(1,1)
    Cell cell11 = row1.createCell(0);
    // 单元格(1,1)写入内容
    cell11.setCellValue("今日新增观众");
    // 创建单元格(1,2)
    Cell cell12 = row1.createCell(1);
    // 单元格(1,2)写入内容
    cell12.setCellValue(666);

    // 第二行
    Row row2 = sheet.createRow(1);
    Cell cell21 = row2.createCell(0);
    cell21.setCellValue("统计时间");
    Cell cell22 = row2.createCell(1);
    String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
    cell22.setCellValue(time);

    // 生成一张表(IO流) 03 版本就是使用xls结尾
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "统计表07版.xlsx");
    // 写出文件
    workbook.write(fileOutputStream);
    // 关闭流
    fileOutputStream.close();

    System.out.println("文件生成完毕");

}

可见 若生成07版只需要把Workbook workbook = new HSSFWorkbook();改为 Workbook workbook = new XSSFWorkbook(); 并且生成的文件后缀改为xlsx就可以了 ,这就是面向借口编程的好处

1.6 (HSSF) 03版写入大量数据

默认写入65536行数据, 查看所需要的时间

// 生成文件路径定义
String PATH = "/home/zpk/DEV/idea/workspace/poi/";

@Test
public void testWrite03BigData() throws Exception {

    long begin = System.currentTimeMillis(); // 开始时间

    // 创建一个簿
    Workbook workbook = new HSSFWorkbook();
    // 创建表
    Sheet sheet = workbook.createSheet();
    // 写入数据
    for (int rowNum = 0; rowNum < 65536; rowNum++) {
        // 一共65536行
        Row row = sheet.createRow(rowNum);
        for (int cellNum = 0; cellNum < 10; cellNum++) {
            // 每列十条数据
            Cell cell = row.createCell(cellNum);
            cell.setCellValue(cellNum);
        }
    }
    System.out.println("over");
    // 输出流
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "bigData03");
    // 输出
    workbook.write(fileOutputStream);
    // 关闭流
    fileOutputStream.close();

    long end = System.currentTimeMillis(); // 结束时间
    // 时间结算
    System.out.println((double)(end - begin)/1000 + " s");
}

执行结果: 总共耗时2.036秒

over
2.036 s

注意: 03版如果行数大于65536则抛出异常

1.7 (XSSF) 07版写入大量数据

// 生成文件路径定义
String PATH = "/home/zpk/DEV/idea/workspace/poi/";

@Test
public void testWrite07BigData() throws Exception {
    // 时间
    long begin = System.currentTimeMillis();

    // 创建一个簿
    Workbook workbook = new XSSFWorkbook();
    // 创建表
    Sheet sheet = workbook.createSheet();
    // 写入数据
    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("over");
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "bigData07.xlsx");
    workbook.write(fileOutputStream);
    fileOutputStream.close();

    long end = System.currentTimeMillis();
    System.out.println((double)(end - begin)/1000 + " s");
}

执行结果: 总共耗时8.015 秒

over
8.015 s

1.8 (SXSSF) 写入大量数据

// 生成文件路径定义
String PATH = "/home/zpk/DEV/idea/workspace/poi/";

@Test
public void testWriteSXSSFBigData() throws Exception {
    // 时间
    long begin = System.currentTimeMillis();

    // 创建一个簿
    Workbook workbook = new SXSSFWorkbook();
    // 创建表
    Sheet sheet = workbook.createSheet();
    // 写入数据
    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("over");
    FileOutputStream fileOutputStream = new FileOutputStream(PATH + "bigDataSXSSF.xlsx");
    workbook.write(fileOutputStream);
    fileOutputStream.close();

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

    long end = System.currentTimeMillis();
    System.out.println((double)(end - begin)/1000 + " s");
}

结果: 2.192 秒

over
2.192 s

注意: 使用SXSSFWorkbook时需要清理临时文件

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

1.9 写入大量数据结论

  • HSSF 写入大量数据v

    • 优点: 可以写入快速写入数据
    • 缺点: 只能写入65536行数据, 非常有限
  • XSSF 写入大量数据

    • 优点: 可以写入任意条数据

    • 缺点: 写入速度慢的一批

  • SXSSF 写入大量数据

    • 优点: 快速而且占用内存少
    • 缺点: 仍然可能会消耗大量内存, 这些内存基于正在使用的功能,例如合并区域, 注释仍然会储存在内存中, 因此如果广泛使用, 可能需要大量的内存

2. 读取

2.1读取文件演示(03版)

// 路径
String PATH = "/home/zpk/DEV/idea/workspace/poi/";

@Test
public void testRead03() throws Exception {
    // 获取文件流
    FileInputStream inputStream = new FileInputStream(PATH + "统计表03版.xls");
    // 1. 创建一个工作簿
    Workbook workbook = new HSSFWorkbook(inputStream);
    // 2. 获取一个工作表, getSheetAt()方法是根据下标获取
    Sheet sheet = workbook.getSheetAt(0);
    // 3. 得到行
    Row row = sheet.getRow(0);
    // 4. 得到单元格
    Cell cell = row.getCell(0);

    // 显示(1,1)的内容, 获取字符串
    System.out.println(cell.getStringCellValue());

    // 关闭流
    inputStream.close();
}

结果: (获取到了上面写入中的1.4中生成的文件)

今日新增观众

2.2 读取文件演示(07版)

// 路径
String PATH = "/home/zpk/DEV/idea/workspace/poi/";

@Test
public void testRead07() throws Exception {
    // 获取文件流
    FileInputStream inputStream = new FileInputStream(PATH + "统计表07版.xlsx");
    // 1. 创建一个工作簿
    Workbook workbook = new XSSFWorkbook(inputStream);
    // 2. 获取一个工作表, getSheetAt()方法是根据下标获取
    Sheet sheet = workbook.getSheetAt(0);
    // 3. 得到行
    Row row = sheet.getRow(0);
    // 4. 得到单元格
    Cell cell = row.getCell(1);

    // 显示(1,1)的内容, 获取数字类型
    System.out.println(cell.getNumericCellValue());

    // 关闭流
    inputStream.close();
}

结果: (获取到了上面写入中的1.5中生成的文文件)

666.0

2.3 获取一行的数据(重点)

Row rows = sheet.getRow(0); // 获取第一行
if (rows != null) {
    int cellCount = rows.getPhysicalNumberOfCells(); // 计数,次行多少列存入cellCount中
    for (int cellNum = 0; cellNum < cellCount; cellNum++){ // 遍历列
        Cell cell = rows.getCell(cellNum); // 获取每一列
        if (cell != null) {
            int cellType = cell.getCellType(); // 每一列的数据类型(枚举)
            String cellValue = cell.getStringCellValue(); // 获取字符串内容
            System.out.print(cellValue + " | "); // 分割线
        }
    }
    System.out.println() // 换行
}

2.4 获取整张表的内容

(工具类), 只需要传入输入流即可

// 提取为工具类
public void read07Excel(FileInputStream inputStream) throws Exception {
    // 1. 创建一个工作簿
    Workbook workbook = new XSSFWorkbook(inputStream);
    // 2. 获取一个工作表, getSheetAt()方法是根据下标获取
    Sheet sheet = workbook.getSheetAt(0);


    Row rows = sheet.getRow(0); // 获取第一行
    if (rows != null) {
        int cellCount = rows.getPhysicalNumberOfCells(); // 计数,次行多少列存入cellCount中
        for (int cellNum = 0; cellNum < cellCount; cellNum++){ // 遍历列
            Cell cell = rows.getCell(cellNum); // 获取每一列
            if (cell != null) {
                int cellType = cell.getCellType(); // 每一列的数据类型(枚举)
                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 = rows.getPhysicalNumberOfCells();
            for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                System.out.println("[" + (rowNum + 1) + "-" + (cellNum + 1) + "]");

                Cell cell = rowData.getCell(cellNum);
                // 匹配列的数据类型
                if (cell != null) {
                    int cellType = cell.getCellType();
                    String cellValue = "";

                    switch (cellType) {
                        case HSSFCell.CELL_TYPE_STRING : // 字符串
                            System.out.print("[String]");
                            cellValue = cell.getStringCellValue();
                            break;
                        case HSSFCell.CELL_TYPE_BOOLEAN : // 布尔型
                            System.out.print("[Boolean]");
                            cellValue = String.valueOf(cell.getStringCellValue());
                            break;
                        case HSSFCell.CELL_TYPE_BLANK : // 空
                            System.out.print("[Blank]");
                            break;
                        case HSSFCell.CELL_TYPE_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(HSSFCell.CELL_TYPE_STRING);
                                cellValue = String.valueOf(cell.getStringCellValue());
                                cellValue = cell.toString();
                            }
                            break;
                        case HSSFCell.CELL_TYPE_ERROR : // 布尔型
                            System.out.print("[数据类型错误]");
                            break;
                    }
                    System.out.println(cellValue);
                }
            }
        }
    }

    // 关闭流
    inputStream.close();
}

EasyExcel

这是阿里云的EasyExcel, 性能和读写速度都很不错

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

github地址:https://github.com/alibaba/easyexcel

pom依赖

<dependency>   
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.0-beta2</version>
</dependency>

官方文档已经写的很详细, 这里就不一一列举了. v

上一篇:Python xlwings


下一篇:杨涛老师MvcPager示例