• POI 和 EasyExcel 操作 Excel


    一、概述

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

    1.1 POI 简介

            Apache POI 是用 Java 编写的免费开源的跨平台的 Java APIApache POI 提供 API Java 程序对 Microsoft Office 格式文档读和写的常用功能。POI “Poor Obfuscation Implementation” 的首字母缩写,意为“简洁版的模糊实现”。其常用的结构如下:

            HSSF -- 提供读写 03 版本的 Excel 常用功能。

            XSSF -- 提供读写 07 版本的 Excel 常用功能。

            HWPF -- 提供读写 Word 格式的常用功能

            HSLF -- 提供读写 ppt 格式的常用功能。

            HDGF -- 提供读写 visio 格式的常用功能

    1.2 easyExcel 简介

            easyExcel 是阿里巴巴开源的一个 excel 处理框架,以使用简单、节省内存著称。easyExcel 能大大减少占用内存的主要原因是在解析 Excel 时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

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

    1.3 xls 和 xlsx 区别

            常用的 excel 文档有两种结尾形式,分别为 xlsxlsx,其中以 xls 结尾的文档属于 03 版本的,它里面最多可以存储 65536 行数据。而以 xlsx 结尾的文档属于 07 版本的,它理论上可以存储无限行数据,这就是两者之前的区别。

    二、POI 常用操作

    2.1 添加 maven 依赖

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.apache.poigroupId>
    4. <artifactId>poiartifactId>
    5. <version>3.9version>
    6. dependency>
    7. <dependency>
    8. <groupId>org.apache.poigroupId>
    9. <artifactId>poi-ooxmlartifactId>
    10. <version>3.9version>
    11. dependency>
    12. <dependency>
    13. <groupId>joda-timegroupId>
    14. <artifactId>joda-timeartifactId>
    15. <version>2.10.1version>
    16. dependency>
    17. <dependency>
    18. <groupId>junitgroupId>
    19. <artifactId>junitartifactId>
    20. <version>4.12version>
    21. dependency>
    22. dependencies>

    2.2 写入 Excel 操作

    2.2.1 一般文件写入

    2.2.1.1 03 版本
    1. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    2. import org.apache.poi.ss.usermodel.Cell;
    3. import org.apache.poi.ss.usermodel.Row;
    4. import org.apache.poi.ss.usermodel.Sheet;
    5. import org.apache.poi.ss.usermodel.Workbook;
    6. import org.joda.time.DateTime;
    7. import org.junit.Test;
    8. import java.io.FileOutputStream;
    9. public class ExcelWriteTest {
    10. String PATH ="F:\\idea_home\\poi-excel\\";
    11. @Test
    12. public void testWrite03() throws Exception {
    13. // 1、创建一个工作簿
    14. Workbook workbook = new HSSFWorkbook();
    15. // 2、创建一个工作表
    16. Sheet sheet = workbook.createSheet("我是 sheet1 页");
    17. // 3、创建一行
    18. Row row1 = sheet.createRow(0);
    19. // 4、创建一个单元格
    20. Cell cell11 = row1.createCell(0);
    21. cell11.setCellValue("我是第一行第一个单元格");
    22. Cell cell12 = row1.createCell(1);
    23. cell12.setCellValue("我是第一行第二个单元格");
    24. // 第二行
    25. Row row2 = sheet.createRow(1);
    26. Cell cell21 = row2.createCell(0);
    27. cell21.setCellValue("我是第二行第一个单元格");
    28. Cell cell22 = row2.createCell(1);
    29. String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
    30. cell22.setCellValue(time);
    31. // 03 版本的使用 xls 结尾
    32. FileOutputStream fileOutputStream = new FileOutputStream(PATH+"统计表03类型.xls");
    33. workbook.write(fileOutputStream);
    34. fileOutputStream.close();
    35. System.out.println("Excel03 写入完成了");
    36. }
    37. }

    2.2.2.2 07 版本
    1. import org.apache.poi.ss.usermodel.Cell;
    2. import org.apache.poi.ss.usermodel.Row;
    3. import org.apache.poi.ss.usermodel.Sheet;
    4. import org.apache.poi.ss.usermodel.Workbook;
    5. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    6. import org.joda.time.DateTime;
    7. import org.junit.Test;
    8. import java.io.FileOutputStream;
    9. public class ExcelWriteTest {
    10. String PATH ="F:\\idea_home\\poi-excel\\";
    11. @Test
    12. public void testWrite07() throws Exception {
    13. // 1、创建一个工作簿
    14. Workbook workbook = new XSSFWorkbook();
    15. // 2、创建一个工作表
    16. Sheet sheet = workbook.createSheet("我是 sheet1 页");
    17. // 3、创建一行
    18. Row row1 = sheet.createRow(0);
    19. // 4、创建一个单元格
    20. Cell cell11 = row1.createCell(0);
    21. cell11.setCellValue("我是第一行第一个单元格");
    22. Cell cell12 = row1.createCell(1);
    23. cell12.setCellValue("我是第一行第二个单元格");
    24. // 第二行
    25. Row row2 = sheet.createRow(1);
    26. Cell cell21 = row2.createCell(0);
    27. cell21.setCellValue("我是第二行第一个单元格");
    28. Cell cell22 = row2.createCell(1);
    29. String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
    30. cell22.setCellValue(time);
    31. // 07 版本的使用 xlsx 结尾
    32. FileOutputStream fileOutputStream = new FileOutputStream(PATH+"统计表07类型.xlsx");
    33. workbook.write(fileOutputStream);
    34. fileOutputStream.close();
    35. System.out.println("Excel07 写入完成了");
    36. }
    37. }

    2.2.2 大文件写入

    2.2.2.1 03 版本
    1. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    2. import org.apache.poi.ss.usermodel.Cell;
    3. import org.apache.poi.ss.usermodel.Row;
    4. import org.apache.poi.ss.usermodel.Sheet;
    5. import org.apache.poi.ss.usermodel.Workbook;
    6. import org.junit.Test;
    7. import java.io.FileOutputStream;
    8. public class ExcelWriteTest {
    9. String PATH ="F:\\idea_home\\poi-excel\\";
    10. @Test
    11. public void testWrite03BigData() throws Exception {
    12. long begin = System.currentTimeMillis();
    13. // 1、创建一个工作簿
    14. Workbook workbook = new HSSFWorkbook();
    15. // 2、创建一个工作表
    16. Sheet sheet = workbook.createSheet();
    17. // 3、写入数据
    18. for(int rowNum =0;rowNum<65537;rowNum++){
    19. Row row = sheet.createRow(rowNum);
    20. for(int cellNum=0;cellNum<10;cellNum++){
    21. Cell cell = row.createCell(cellNum);
    22. cell.setCellValue(cellNum);
    23. }
    24. }
    25. System.out.println("over");
    26. FileOutputStream fileOutputStream = new FileOutputStream(PATH+"统计表03大数据类型.xls");
    27. workbook.write(fileOutputStream);
    28. fileOutputStream.close();
    29. long end = System.currentTimeMillis();
    30. System.out.println((double)(end-begin)/1000);
    31. }
    32. }

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

            优点:写入过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快。将 65537 改成65536 再次执行程序,结果如下,可以看到 1.692s 就完成了写入操作,速度还是很快的。

    2.2.2.2 07 版本
    1. import org.apache.poi.ss.usermodel.Cell;
    2. import org.apache.poi.ss.usermodel.Row;
    3. import org.apache.poi.ss.usermodel.Sheet;
    4. import org.apache.poi.ss.usermodel.Workbook;
    5. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    6. import org.junit.Test;
    7. import java.io.FileOutputStream;
    8. public class ExcelWriteTest {
    9. String PATH ="F:\\idea_home\\poi-excel\\";
    10. @Test
    11. public void testWrite07BigData() throws Exception {
    12. long begin = System.currentTimeMillis();
    13. // 1、创建一个工作簿
    14. Workbook workbook = new XSSFWorkbook();
    15. // 2、创建一个工作表
    16. Sheet sheet = workbook.createSheet();
    17. // 3、写入数据
    18. for(int rowNum =0;rowNum<100000;rowNum++){
    19. Row row = sheet.createRow(rowNum);
    20. for(int cellNum=0;cellNum<10;cellNum++){
    21. Cell cell = row.createCell(cellNum);
    22. cell.setCellValue(cellNum);
    23. }
    24. }
    25. System.out.println("over");
    26. FileOutputStream fileOutputStream = new FileOutputStream(PATH+"统计表07大数据类型.xlsx");
    27. workbook.write(fileOutputStream);
    28. fileOutputStream.close();
    29. long end = System.currentTimeMillis();
    30. System.out.println((double)(end-begin)/1000);
    31. }
    32. }

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

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

    2.2.2.3 07 版本优化
    1. import org.apache.poi.ss.usermodel.Cell;
    2. import org.apache.poi.ss.usermodel.Row;
    3. import org.apache.poi.ss.usermodel.Sheet;
    4. import org.apache.poi.ss.usermodel.Workbook;
    5. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
    6. import org.junit.Test;
    7. import java.io.FileOutputStream;
    8. public class ExcelWriteTest {
    9. String PATH ="F:\\idea_home\\poi-excel\\";
    10. @Test
    11. public void testWrite07BigDataS() throws Exception {
    12. long begin = System.currentTimeMillis();
    13. // 1、创建一个工作簿
    14. Workbook workbook = new SXSSFWorkbook();
    15. // 2、创建一个工作表
    16. Sheet sheet = workbook.createSheet();
    17. // 3、写入数据
    18. for(int rowNum =0;rowNum<100000;rowNum++){
    19. Row row = sheet.createRow(rowNum);
    20. for(int cellNum=0;cellNum<10;cellNum++){
    21. Cell cell = row.createCell(cellNum);
    22. cell.setCellValue(cellNum);
    23. }
    24. }
    25. System.out.println("over");
    26. FileOutputStream fileOutputStream = new FileOutputStream(PATH+"统计表07大数据类型优化.xlsx");
    27. workbook.write(fileOutputStream);
    28. // 清除产生的临时文件
    29. ((SXSSFWorkbook)workbook).dispose();
    30. fileOutputStream.close();
    31. long end = System.currentTimeMillis();
    32. System.out.println((double)(end-begin)/1000);
    33. }
    34. }

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

            需要注意的是:代码在过程中会产生临时文件,需要清理临时文件。默认有 100 条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件。如果想自定义内存中数据的数量,可以使用 new SXSSFWorkbook(数量) 。

            SXSSFWorkbook 来至官方的解释:实现 “BigGridDemo” 策略的流式 XSSFWorkbook 版本。这允许写入非常大的文件而不会耗尽内存,因为任何时候只有可配置的行部分被保存在内存中。请注意,仍然可能会消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注释……仍然只存储在内存中,因此如果广泛使用,可能需要大量内存。

    2.3 读取 Excel 操作

    2.3.1 03 版本

    1. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    2. import org.apache.poi.ss.usermodel.*;
    3. import org.junit.Test;
    4. import java.io.FileInputStream;
    5. public class ExcelRead {
    6. String PATH ="F:\\idea_home\\poi-excel\\";
    7. @Test
    8. public void testRead03() throws Exception {
    9. // 1、获取文件流
    10. FileInputStream fileInputStream = new FileInputStream(PATH+"统计表03类型.xls");
    11. // 2、创建文件簿,使用 excel 能操作的这边都可以操作
    12. Workbook workbook = new HSSFWorkbook(fileInputStream);
    13. // 3、得到表
    14. Sheet sheet = workbook.getSheetAt(0);
    15. // 4、得到行
    16. Row row = sheet.getRow(0);
    17. // 5、得到列
    18. Cell cell = row.getCell(0);
    19. // 读取值的时候需要注意类型,String 和数字调用的方法是不同的。
    20. System.out.println(cell.getStringCellValue());
    21. fileInputStream.close();
    22. }
    23. }

    2.3.2 07 版本

    1. import org.apache.poi.ss.usermodel.*;
    2. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    3. import org.junit.Test;
    4. import java.io.FileInputStream;
    5. public class ExcelRead {
    6. String PATH ="F:\\idea_home\\poi-excel\\";
    7. @Test
    8. public void testRead07() throws Exception {
    9. // 1、获取文件流
    10. FileInputStream fileInputStream = new FileInputStream(PATH+"统计表07类型.xlsx");
    11. // 2、创建文件簿,使用 excel 能操作的这边都可以操作
    12. Workbook workbook = new XSSFWorkbook(fileInputStream);
    13. // 3、得到表
    14. Sheet sheet = workbook.getSheetAt(0);
    15. // 4、得到行
    16. Row row = sheet.getRow(0);
    17. // 5、得到列
    18. Cell cell = row.getCell(1);
    19. // 读取值的时候需要注意类型,String 和数字调用的方法是不同的。
    20. System.out.println(cell.getStringCellValue());
    21. fileInputStream.close();
    22. }
    23. }

    2.3.3 读取不同类型

            表格的内容如下所示:

            代码如下所示:

    1. import org.apache.poi.hssf.usermodel.HSSFCell;
    2. import org.apache.poi.hssf.usermodel.HSSFDateUtil;
    3. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    4. import org.apache.poi.ss.usermodel.*;
    5. import org.joda.time.DateTime;
    6. import org.junit.Test;
    7. import java.io.FileInputStream;
    8. import java.util.Date;
    9. public class ExcelRead {
    10. String PATH ="F:\\idea_home\\poi-excel\\";
    11. @Test
    12. public void testCellType() throws Exception{
    13. // 1、获取文件流
    14. FileInputStream fileInputStream = new FileInputStream(PATH+"03商品信息.xls");
    15. // 2、创建文件簿,使用 excel 能操作的这边都可以操作
    16. Workbook workbook = new HSSFWorkbook(fileInputStream);
    17. // 3、得到表
    18. Sheet sheet = workbook.getSheetAt(0);
    19. // 4、获取标题内容
    20. Row rowTitle = sheet.getRow(0);
    21. if(rowTitle != null){
    22. // 获取列的数量
    23. int cellCount = rowTitle.getPhysicalNumberOfCells();
    24. for(int cellNum=0;cellNum
    25. Cell cell = rowTitle.getCell(cellNum);
    26. if(cell != null){
    27. // 获取列的类型
    28. int cellType = cell.getCellType();
    29. // 获取具体的列名
    30. String cellValue = cell.getStringCellValue();
    31. System.out.print(cellValue+" | ");
    32. }
    33. }
    34. }
    35. System.out.println();
    36. // 5、获取表中的内容
    37. // 获取有多少行的记录
    38. int rowCount = sheet.getPhysicalNumberOfRows();
    39. for(int rowNum=1;rowNum
    40. // 获取第一行数据
    41. Row rowData = sheet.getRow(rowNum);
    42. if(rowData !=null){
    43. // 读取行中的列
    44. int cellCount = rowTitle.getPhysicalNumberOfCells();
    45. for(int cellNum=0;cellNum
    46. System.out.print("["+(rowNum+1)+"-"+(cellNum+1)+"]");
    47. Cell cell = rowData.getCell(cellNum);
    48. // 匹配类的数据类型
    49. if(cell != null){
    50. int cellType = cell.getCellType();
    51. String cellValue="";
    52. switch(cellType){
    53. case HSSFCell.CELL_TYPE_STRING: //字符串
    54. System.out.print("【STRING】");
    55. cellValue = cell.getStringCellValue();
    56. break;
    57. case HSSFCell.CELL_TYPE_BOOLEAN: //布尔
    58. System.out.print("【BOOLEAN】");
    59. cellValue = String.valueOf(cell.getBooleanCellValue());
    60. break;
    61. case HSSFCell.CELL_TYPE_BLANK: //空
    62. System.out.print("【BLANK】");
    63. break;
    64. case HSSFCell.CELL_TYPE_NUMERIC: //数字(分为日期和普通数字)
    65. System.out.print("【NUMERIC】");
    66. if(HSSFDateUtil.isCellDateFormatted(cell)){ // 日期
    67. System.out.print("【日期】");
    68. Date date = cell.getDateCellValue();
    69. cellValue = new DateTime(date).toString("yyyy-MM-dd");
    70. }else{
    71. // 非日期格式,转换成字符串格式
    72. System.out.print("【转化为字符串输出】");
    73. cell.setCellType(HSSFCell.CELL_TYPE_STRING);
    74. cellValue = cell.toString();
    75. }
    76. break;
    77. case HSSFCell.CELL_TYPE_ERROR: //字符串
    78. System.out.print("【数据类型错误】");
    79. break;
    80. }
    81. System.out.println(cellValue);
    82. }
    83. }
    84. }
    85. }
    86. fileInputStream.close();
    87. }
    88. }

    2.3.4 读取公式

            操作的表格内容如下所示:

            代码如下所示:

    1. import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
    2. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    3. import org.apache.poi.ss.usermodel.*;
    4. import org.junit.Test;
    5. import java.io.FileInputStream;
    6. public class ExcelRead {
    7. String PATH ="F:\\idea_home\\poi-excel\\";
    8. @Test
    9. public void testFormula() throws Exception {
    10. FileInputStream fileInputStream = new FileInputStream(PATH + "03求和.xls");
    11. // 1.创建一个工作簿。使得excel能操作的,这边他也能操作。
    12. Workbook workbook = new HSSFWorkbook(fileInputStream);
    13. // 2.得到表。
    14. Sheet sheet = workbook.getSheetAt(0);
    15. Row row = sheet.getRow(6);
    16. Cell cell = row.getCell(0);
    17. // 拿到计算公司
    18. FormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook);
    19. // 输出单元格内容
    20. int cellType = cell.getCellType();
    21. switch (cellType){
    22. case Cell.CELL_TYPE_FORMULA:
    23. String cellFormula = cell.getCellFormula();
    24. System.out.println(cellFormula);
    25. // 计算
    26. CellValue evaluate = formulaEvaluator.evaluate(cell);
    27. String cellValue = evaluate.formatAsString();
    28. System.out.println(cellValue);
    29. break;
    30. }
    31. }
    32. }

    三、EasyExcel 常用操作

    3.1 添加 maven 依赖

    1. <dependencies>
    2. <dependency>
    3. <groupId>com.alibabagroupId>
    4. <artifactId>easyexcelartifactId>
    5. <version>3.0.5version>
    6. dependency>
    7. <dependency>
    8. <groupId>lambadagroupId>
    9. <artifactId>lambadaartifactId>
    10. <version>1.0.3version>
    11. dependency>
    12. <dependency>
    13. <groupId>org.projectlombokgroupId>
    14. <artifactId>lombokartifactId>
    15. <version>RELEASEversion>
    16. <scope>compilescope>
    17. dependency>
    18. <dependency>
    19. <groupId>junitgroupId>
    20. <artifactId>junitartifactId>
    21. <version>4.12version>
    22. dependency>
    23. <dependency>
    24. <groupId>com.alibabagroupId>
    25. <artifactId>fastjsonartifactId>
    26. <version>1.2.7version>
    27. dependency>
    28. dependencies>

    3.2 写操作

            先模拟一个实体类,如下所示:

    1. import com.alibaba.excel.annotation.ExcelIgnore;
    2. import com.alibaba.excel.annotation.ExcelProperty;
    3. import lombok.EqualsAndHashCode;
    4. import lombok.Getter;
    5. import lombok.Setter;
    6. import java.util.Date;
    7. @Getter
    8. @Setter
    9. @EqualsAndHashCode
    10. public class DemoData {
    11. @ExcelProperty("字符串标题")
    12. private String string;
    13. @ExcelProperty("日期标题")
    14. private Date date;
    15. @ExcelProperty("数字标题")
    16. private Double doubleData;
    17. /**
    18. * 忽略这个字段
    19. */
    20. @ExcelIgnore
    21. private String ignore;
    22. }

            然后写入文档即可,如下所示: 

    1. import com.alibaba.excel.EasyExcel;
    2. import com.alibaba.excel.util.ListUtils;
    3. import org.junit.Test;
    4. import java.util.Date;
    5. import java.util.List;
    6. public class TestWrite {
    7. @Test
    8. public void simpleWrite() {
    9. String PATH ="F:\\idea_home\\poi-excel\\easyexcel统计表03类型.xlsx";
    10. EasyExcel.write(PATH, DemoData.class)
    11. .sheet("模板")
    12. .doWrite(() -> {
    13. // 分页查询数据
    14. return data();
    15. });
    16. }
    17. private List data() {
    18. List list = ListUtils.newArrayList();
    19. for (int i = 0; i < 10; i++) {
    20. DemoData data = new DemoData();
    21. data.setString("字符串" + i);
    22. data.setDate(new Date());
    23. data.setDoubleData(0.56);
    24. list.add(data);
    25. }
    26. return list;
    27. }
    28. }

    3.3 读操作

    1. import lombok.EqualsAndHashCode;
    2. import lombok.Getter;
    3. import lombok.Setter;
    4. import java.util.Date;
    5. @Getter
    6. @Setter
    7. @EqualsAndHashCode
    8. public class DemoData {
    9. private String string;
    10. private Date date;
    11. private Double doubleData;
    12. }
    1. import com.alibaba.excel.EasyExcel;
    2. import com.alibaba.excel.read.listener.PageReadListener;
    3. import com.alibaba.fastjson.JSON;
    4. import org.junit.Test;
    5. public class TestRead {
    6. @Test
    7. public void simpleRead() {
    8. String PATH ="F:\\idea_home\\poi-excel\\easyexcel统计表03类型.xlsx";
    9. // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
    10. // 这里每次会读取3000条数据 然后返回过来 直接调用使用数据就行
    11. EasyExcel.read(PATH, DemoData.class, new PageReadListener(dataList -> {
    12. for (DemoData demoData : dataList) {
    13. System.out.println(JSON.toJSONString(demoData));
    14. }
    15. })).sheet().doRead();
    16. }
    17. }

    3.4 更多操作

            详细的文档地址:https://www.yuque.com/easyexcel/doc/easyexcel

  • 相关阅读:
    C++实践2:在c++20中为spdlog与fmt装配source_location
    第1章 现代通信网概述
    通过WinSCP实现Windows给Ubuntu(Linux)虚拟机传输数据
    nvidia-smi指令报错:Failed to initialize NVML: Driver 解决
    【Go入门】struct类型
    Centos - DHCP 服务器搭建
    vue导出功能实现
    git 的功能使用(三)
    python:neat-python遗传拓扑神经网络初步使用
    日本MF备案注册数据库-在线免费查询
  • 原文地址:https://blog.csdn.net/xhf852963/article/details/133673080