Java处理Excel表格(POI、EasyExcel)

一:Java POI 基本简介

Apache POI(Poor Obfuscation Implementation) 是用Java编写的免费开源的跨平台的Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能,其中使用最多的就是使用POI操作Excel文件。

它是创建和维护操作各种符合Office Open XML(OOXML)标准和微软的OLE2复合文档格式的Java API。用它可以使用Java读取、创建和修改MS Excel文件。而且还可以使用Java读取和创建MS Word文件和MS PowerPoint文件。Apache POI提供Java操作Excel解决方案(适用于 97-2003 或 2007)
OOXML格式主要包括以下几种文件类型:

SpreadsheetML(Excel表格如 .xlsx、.xls):存储Microsoft Excel电子表格的XML格式。

WordprocessingML(Word文档如 .docx、.doc):存储Microsoft Word文档的XML格式。

PresentationML(PPT演示稿如 .pptx、.ppt):存储Microsoft PowerPoint演示文稿的XML格式。
Apache POI - 组件概述Apache POI 5.0 - API文档

注:本文主要讲解.xlsx的2007版操作,而.xls的97-2003老版本使用起来和2007版差不多,而且老版本的使用也越来越少。

POI发行版可以对许多文档文件格式的支持。这种支持是在几个JAR文件中提供的。并不是每种格式都需要所有的JAR。下面我介绍一下POI组件、Maven存储库标记和项目的Jar文件之间的关系。

java 复制代码
'组件'                   '操作格式'                '依赖'              '说明'
XSSF(读写*.xlsx 文件)   Excel XLSX               poi-ooxml
XSLF(处理*.pptx 文件)   PowerPoint PPTX          poi-ooxml
XWPF(处理*.docx 文件)   Word DOCX                poi-ooxml
XDGF(处理*.vsdx 文件)   Visio VSDX               poi-ooxml
HSLF(处理*.ppt文件)     PowerPoint PPT           poi-scratchpad
HWPF(处理*.doc文件)     Word DOC                 poi-scratchpad
HDGF(处理*.vsd文件)     Visio VSD                poi-scratchpad
HSSF(读写*.xls文件)     Excel XLS                poi                仅支持HSSF(只可以操作XLS)
POIFS                    OLE2 Filesystem          poi                需要处理基于OLE2/POIFS的文件
HPSF                     OLE2 Property Sets       poi
DDF                      Escher common drawings   poi
HPBF                     Publisher PUB            poi-scratchpad
HSMF                     Outlook MSG              poi-scratchpad
HWMF                     WMF drawings             poi-scratchpad

特殊:
    Common SL:        PPT和PPTX        poi-scratchpad 和 poi-ooxml
                    SL代码在核心POIjar中,但是实现的是poi-scratchpad和poi-ooxml
    Common SS:        XLS和XLSX                 poi-ooxml
                    WorkbookFactory和其它实现都需要poi-ooxml,而不仅仅是核心po
    OpenXML4J:        OOXML         poi-ooxml和poi-ooxml-lite或poi-ooxml-full

<math xmlns="http://www.w3.org/1998/Math/MathML"> m a v e n 关于 P O I 操作的整套依赖 \color{#f00}{maven关于POI操作的整套依赖} </math>maven关于POI操作的整套依赖

xml 复制代码
<dependencies>
   <!--注意:4.x或5.x的版本jdk编译要必须1.8及以上 -->
   <!--Apache POI 基本依赖坐标(处理Excel表格、Word文档、PPT演示稿)-->
   <!--低版本97-2003的文档处理如:.xls、.ppt、.doc需要导入此坐标即可-->
   <dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi</artifactId>
     <version>5.2.5</version>
   </dependency>
   <!--高版本2007的文档处理如:.xlsx、.pptx、.docx需要导入此坐标即可-->
   <dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi-ooxml</artifactId>
     <version>5.2.5</version>
   </dependency>
   <!--若有导入导出百万需求的数据则需要导入此坐标-->
   <dependency>
     <groupId>org.apache.poi</groupId>
     <artifactId>poi-scratchpad</artifactId>
     <version>5.2.5</version>
   </dependency>
</dependencies>

二:POI操作Excel(.xlsx、.xls)

说到Excel的表格操作就必须聊到低版本(97-2003)和高版本(2007+),其主要区别是低版本就是.xls,它最大支持65536条记录和256列,而高版本就是.xlsx,它最大支持记录为1048576条记录和16384列。

java 复制代码
低版本中的类名     高版本中的类名        对应Excel名称
HSSFWorkbook      XSSFWorkbook        工作簿
HSSFSheet         XSSFSheet           工作表
HSSFRow           XSSFRow             行
HSSFCell          XSSFCell            单元格(列)
HSSFCellStyle     XSSFCellStyle       单元格样式
"注:低版本和高版本的实现类是不一样的,但具体的操作方式都是原因的"

(一):Excel基本创建和读取

操作Excel的.xlsx和.xls文件则需要使用工作簿Workbook接口,它内部有着许多的实现类,按照类型可分为如:HSSFWorkbook(处理.xls)、XSSFWorkbook(处理.xlsx)、SXSSFWorkbook(处理批量读写)等。   下面是一个创建和读取Excel的基本流程,并且都是.xlsx类型的,其实需要变为.xls类型的则只需要把那些类名称前缀为XSSF的替换为HSSF即可。

java 复制代码
public static void main(String[] args) throws IOException {
        // 创建Excel文件
        createExcel();
        // 读取刚才创建Excel文件
        readExcel();
    }

    /***
     * 创建Excel(类型为.xlsx)
     */
    public static void createExcel() throws IOException {
        // 1:创建.xlsx类型的Excel工作簿
        XSSFWorkbook workbook = new XSSFWorkbook();

        // 2:创建工作表  就是Excel文件最下边的sheet1、sheet2工作表
        XSSFSheet sheet = workbook.createSheet("sheet(工作表①)");

        // 3:通过下面2行代码定位并创建一个坐标为 0,0 的单元格,并写上内容 对应Excel 1,1 位置
        // 创建行 当前创建的为0行 对应Excel第一行
        XSSFRow row = sheet.createRow(0);
        // 创建列 当前创建列为0列 对应Excel第一列
        XSSFCell cell = row.createCell(0);
        // 写入内容
        cell.setCellValue("测试写入内容!!");
        //或者链式调用写法:sheet.createRow(0).createCell(0).setCellValue("测试写入内容!!");

        // 4:把构建的内存数据写入到磁盘 确保有这个磁盘
        OutputStream out = new FileOutputStream("D://test.xlsx");
        workbook.write(out);

        // 5:关闭资源
        out.close();
        workbook.close();
    }

    /***
     * 读取刚才创建的Excel(类型为.xlsx)
     */
    public static void readExcel() throws IOException {

        // 1:创建.xlsx类型的Excel工作簿
        XSSFWorkbook workbook = new XSSFWorkbook("D://test.xlsx");

        // 2:获取文件的第一个工作表
        XSSFSheet sheetAt = workbook.getSheetAt(0);
        // 注:getSheet(String name); 通过Sheet名称来获取,不怎么用

        // 3:通过下面2行代码定位并获取一个坐标为 0,0 的单元格,并获取内容 对应Excel 1,1 位置
        // 获取行 当前获取的为0行 对应Excel第一行
        XSSFRow row = sheetAt.getRow(0);
        // 获取行 当前获取列为0列 对应Excel第一列
        XSSFCell cell = row.getCell(0);

        // 4:读取单元格内容
        String stringCellValue = cell.getStringCellValue();
        System.out.println("获取单元格0,0的内容为:"+stringCellValue);

        // 5:关闭工作簿
        workbook.close();
    }

(二):创建不同类型的单元格

其实Excel文件里的单元格是有多种类型的,比如:字符串类型、日期类型、数字类型、布尔类型 等等,但是我们不能只通过一个字符串类型来表达全部吧;所以我们就得使用 CellStyle接口方法来操作样式的设置,其实这个接口操作很广泛,如:边框颜色、对齐方式、字体、颜色 等都可以操作;假设要设置日期类型的话,那么这里就得使用 CellStyle 里的 setDataFormat(short fmt) 方法,使用此方法还必须得搭配着接口 CreationHelper,它里面的createDataFormat()方法可以创建DataFormat实例并返回short参数。下面就来看看不同类型的单元格内容如何设置:
点开查看详情:创建不同类型的Excel单元格

java 复制代码
/***
     * 创建多种类型单元格的文件
     */
    public static void multipleTypesCreate() throws IOException {
        //创建工作簿
        Workbook workbook = new XSSFWorkbook();
        //创建工作表  注:不指定名则默认Sheet0、Sheet1、SheetN...
        Sheet sheet = workbook.createSheet();
        // ==========  1:在Excel单元格的第一行第一列写入 日期类型 数据 ==========
        // ■ 错误方式:直接写入是不可以的,会变为小数类型,需要调整类型格式
        // Cell cellA = sheet.createRow(0).createCell(0);
        // cellA.setCellValue(new Date());
        // ■ 正确方式:更改为日期类型
        //  返回一个对象,该对象处理XSSF各种实例的具体类的实例化
        CreationHelper creationHelper = workbook.getCreationHelper();
        //  创建新的DataFormat实例。获取与给定格式字符串匹配的格式索引,在需要时创建一个新的格式条目。
        short format = creationHelper.createDataFormat().getFormat("yyyy-MM-dd");
        //  通过Workbook来获取一个样式操作类来对已有的表设置样式
        CellStyle cellStyle = workbook.createCellStyle();
        //  把日期格式设置当当前样式cellStyle中
        cellStyle.setDataFormat(format);
        //  在第一行第一列位置写入日期数据 并对 0,0 位置写入样式
        Cell cellB = sheet.createRow(0).createCell(0);
        cellB.setCellValue(new Date());
        cellB.setCellStyle(cellStyle);  // 设置单元格类型
        // ========== 2:在Excel单元格的第二行第二列写入 字符串类型 数据 ==========
        sheet.createRow(1).createCell(1).setCellValue("我是字符串");
        // ========== 3:在Excel单元格的第三行第三列写入 数值类型(整数或浮点) 数据 ==========
        CellStyle style = workbook.createCellStyle();
        // 设置数据格式为两位小数
        style.setDataFormat(workbook.createDataFormat().getFormat("0.00"));
        Cell cellC = sheet.createRow(2).createCell(2);
        cellC.setCellValue(22.4634D);
        cellC.setCellStyle(style);
        // ========== 4:在Excel单元格的第四行第四列写入 布尔类型 数据 ==========
        Cell cellD = sheet.createRow(3).createCell(3);
        cellD.setCellValue(true);
        // ========== 5:在Excel单元格的第五行第五列写入 错误类型 数据 ==========
        Cell cellE = sheet.createRow(4).createCell(4);
        cellE.setCellErrorValue(FormulaError.REF.getCode());
        //  一些错误类型的枚举:
        //      DIV0:#DIV/0!,表示除数为零的错误。
        //      VALUE:#VALUE!,表示公式中使用了无效的数值。
        //      REF:#REF!,表示引用不可用的错误。
        //      NAME:#NAME?,表示公式中使用了未知的名称。
        //      NUM:#NUM!,表示公式中使用了无效的数值或数字格式。
        //      NULL:#NULL!,表示公式中存在无效的空值引用。
        //      NA:#N/A,表示未找到匹配项。
        //把构建的内存数据写入到磁盘
        OutputStream out = new FileOutputStream("D://test1.xlsx");
        workbook.write(out);
        //关闭资源
        out.close();
        workbook.close();
    }

我们有了上面的基础知识铺垫以后,可以自己动手写个简单的创建.xlsx或.xls格式的Excel文件了,至于样式需要在后几节讲解;废话不多说,直接上操作:

java 复制代码
/*** 测试基本的Excel表格创建 */
    public static void testCreationXlsx() throws IOException {
        //创建数据源
        Object[] title = {"ID", "姓名", "零花钱", "生日", "是否被删除", "空值引用"};
        Object[] s1 = {"tx01", "张三", 66.663D, new Date(), true, FormulaError.NULL};
        Object[] s2 = {"tx02", "李四", 76.882D, new Date(), false, FormulaError.NULL};
        List<Object[]> list = new ArrayList<>();
        list.add(title);
        list.add(s1);
        list.add(s2);
        // 创建工作簿
        Workbook workbook = new XSSFWorkbook();
        // 创建工作表
        Sheet sheet = workbook.createSheet("总结");
        // 循环行创建
        for (int r = 0; r < list.size(); r++) {
            // 创建行
            Row row = sheet.createRow(r);
            // 循环创建列操作
            for (int c = 0; c < list.get(r).length; c++) {
                // 创建列
                Cell cell = row.createCell(c);
                // 获取每个单元格数据然后匹配类型
                Object o = list.get(r)[c];
                // 此时数据是要改变样式(根据不同的数据类型,写入不同类型的单元格)
                if (o instanceof Date) {
                    // 写入日期类型
                    CellStyle cellStyle = workbook.createCellStyle();
                    cellStyle.setDataFormat(workbook.getCreationHelper()
                            .createDataFormat().getFormat("yyyy-MM-dd"));
                    cell.setCellValue((Date) list.get(r)[c]);
                    cell.setCellStyle(cellStyle);
                } else if (o instanceof Double) {
                    CellStyle cellStyle = workbook.createCellStyle();
                    cellStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00"));
                    cell.setCellValue((Double) list.get(r)[c]);
                    cell.setCellStyle(cellStyle);
                } else if (o instanceof FormulaError) {
                    // 写入错误类型
                    cell.setCellErrorValue(((FormulaError) o).getCode());
                } else {
                    // 写入字符串
                    cell.setCellValue(list.get(r)[c].toString());
                }
            }
        }
        // 写出及关闭
        OutputStream out = new FileOutputStream("D:\\test02.xlsx");
        workbook.write(out);
        out.close();
        workbook.close();
    }

(三):从Excel文件中遍历数据

遍历数据可谓是重点了,我们得创建一个工作簿并传入.xlsx或.xls文件,其中获取起始行和结尾行则使用 getFirstRowNum 和 getLastRowNum 方法,但是获取的每个单元格表格数据类型不同则需要使用类型匹配,下面就用来遍历上面创建的数据表格:

java 复制代码
public static void testReadExcel() throws IOException {
        // 读取的数据都往里面存
        List<Object[]> list = new ArrayList<>();

        // 创建工作簿并传入一个真实存在的文件
        Workbook workbook = new XSSFWorkbook("D:\\test02.xlsx");
        // 获取文件的第一个工作表
        Sheet sheetAt = workbook.getSheetAt(0);

        // 获取工作表中的起始行和结束行
        int rowStart = sheetAt.getFirstRowNum();

        int rowEnd = sheetAt.getLastRowNum();
        // 循环开始行到结束行
        for (int rowNum = rowStart; rowNum <= rowEnd; rowNum++) {
            // 数据存储
            Object[] obj = new Object[6];
            // 获取行实例
            Row row = sheetAt.getRow(rowNum);
            // 一旦判断有空行则跳出本次循环
            if (row == null) {
                continue;
            }

            // 当前行若不为空的话则获取列(获取当前行的结束列)
            int lastColumn = row.getLastCellNum();
            // 获取定义的单元格数(不是实际行中的单元格数!)。
            // 也就是说,如果只有0、4、5列有值,那么就有3列。
            // int lastColumn = row.getPhysicalNumberOfCells();
            // 循环列,从0开始循环到lastColumn
            for (int colNum = 0; colNum <= lastColumn; colNum++) {
                // 获取列实例
                Cell cell = row.getCell(colNum);
                // 若不为空的话则打印数据(因为表格的每个类型不一样,需要特殊处理)
                obj[colNum] = parseDifferentTypesCells(cell);
            }
            list.add(obj);
        }
        // 关闭资源
        workbook.close();
        // 循环数据
        for (Object[] arr : list) {
            for (Object o : arr) {
                System.out.print(o + " | ");
            }
            System.out.println();
        }
        // 打印结果:
        //      ID | 姓名 | 零花钱 | 生日 | 是否被删除 | 空值引用 |
        //      tx01 | 张三 | 66.663 | 2024-02-25 | true | #NULL! |
        //      tx02 | 李四 | 76.882 | 2024-02-25 | false | #NULL! |
    }

    /***
     *  解析不同类型单元格
     * @return Object
     */
    public static Object parseDifferentTypesCells(Cell cell) {
        // 返回的数据
        Object result = null;
        // 若为空则直接返回空
        if (cell == null) {
            return null;
        } else {
            switch (cell.getCellType()) {
                case NUMERIC: // 数字类型
                    // 通过查找StylesSource获取格式字符串的内容
                    String formatString = cell.getCellStyle().getDataFormatString();
                    //判断当前的字符串形式的内容是否以m/d/yy的日期格式
                    if (formatString.equals("m/d/yy")) {
                        result = cell.getDateCellValue();
                    } else if (formatString.equals("yyyy-MM-dd")) {
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                        result = sdf.format(cell.getDateCellValue());
                    } else {
                        result = cell.getNumericCellValue();
                    }
                    break;
                case STRING: // 字符串类型
                    result = cell.getStringCellValue();
                    break;
                case BOOLEAN: // 真假值类型
                    result = cell.getBooleanCellValue();
                    break;
                case BLANK: // 空数据类型
                    result = "空";
                    break;
                case FORMULA: // 计算类型
                    result = cell.getCellFormula();
                    break;
                case _NONE: // 未知类型
                    result = "未知数据";
                    break;
                case ERROR: // 错误类型
                    result = FormulaError.forInt(cell.getErrorCellValue()).getString();
                    break;
                default:
                    return null;
            }
        }
        return result;
    }

(四):设置不同样式单元格

1:单元格对齐方式

当说到对齐方式我们就得谈谈POI提供的两个枚举类了 HorizontalAlignment(水平对齐)VerticalAlignment(垂直对齐),但是我们设置对齐方式还得借助 CellStyle 样式接口内部的实现类完成,通过 setAlignmentsetVerticalAlignment 方法来对单元格设置对齐方式:
点开查看详情:设置单元格的对齐方式

java 复制代码
public static void alignmentMethod() throws IOException {
        // 创建工作簿和工作表(未指定工作表名称则为"Sheet0~N")
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet();
        // 设置行,并指定行高为60
        Row row = sheet.createRow(0);
        row.setHeightInPoints(60);
        // 创建坐标 0,1 的单元格(就是第一行第二列)
        Cell cell = row.createCell(1);
        cell.setCellValue("啦啦");
        // 创建单元格样式
        CellStyle cellStyle = workbook.createCellStyle();
        // 设置单元格的水平对齐类型。 此时水平居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        // 设置单元格的垂直对齐类型。 此时垂直靠底边
        cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
        // 把样式设置到单元格上
        cell.setCellStyle(cellStyle);
        //写出及关闭
        OutputStream out = new FileOutputStream("D:\\test03.xlsx");
        workbook.write(out);
        out.close();
        workbook.close();
    }

2:单元格边框及颜色

感觉设置单元格边框的颜色用处并不大,但要设置漂亮样式可以通过 CellStyle 来设置边框和边框颜色,具体的就得参照 BorderStyle(边框样式)IndexedColors(背景色) 这2个枚举类来获取具体的样式,下面将对其写一个基本实例:
点开查看详情:设置单元格边框颜色及样式

java 复制代码
public static void borderColor() throws IOException {
        // 创建工作簿和创建工作表
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet();
        // 创建单元格行信息
        Row row = sheet.createRow(1);
        // 为了可以看到样式 特地设置单元格高度
        row.setHeightInPoints(60);
        // 创建坐标 1,1 单元格(第二行第二列)
        Cell cell = row.createCell(1);
        cell.setCellValue("吆西");
        // 创建样式(从workbook获取当前样式对象)
        CellStyle cellStyle = workbook.createCellStyle();
        // 通过set设置边框样式及边框颜色  反之通过get可以获取设置的样式
        cellStyle.setBorderBottom(BorderStyle.DOTTED);      // 设置单元格底部样式
        cellStyle.setBorderTop(BorderStyle.THIN);           // 设置单元格顶部样式
        cellStyle.setBorderLeft(BorderStyle.HAIR);          // 设置单元格左边样式
        cellStyle.setBorderRight(BorderStyle.DASH_DOT_DOT); // 设置单元格右边样式
        cellStyle.setBottomBorderColor(IndexedColors.GREEN.getIndex()); // 绿色
        cellStyle.setTopBorderColor(IndexedColors.CORAL.getIndex());    // 珊瑚色
        cellStyle.setLeftBorderColor(IndexedColors.RED.getIndex());     // 红色
        cellStyle.setRightBorderColor(IndexedColors.AQUA.getIndex());   // 水绿色
        // 别忘了把刚才这些设置的样式设置到单元格上
        cell.setCellStyle(cellStyle);
        //写出及关闭
        OutputStream out = new FileOutputStream("D:\\test03.xlsx");
        workbook.write(out);
        out.close();
        workbook.close();
    }

3:单元格颜色填充

有时候我们会设置单元格的前景色以及背景色,甚者会对单元格填充图案,这时候我们就会用到样式类里面的填充方法;关于枚举类:FillPatternType(图案背景)IndexedColors(背景色) 具体看代码:
点开查看详情:设置单元格前景色和背景色

java 复制代码
public static void backgroundColor() throws IOException {
        // 创建工作簿和Sheet0工作表
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet();
        // 设置第一列的宽度是20个字符宽度
        sheet.setColumnWidth(1, 20*256);
        // 创建表格第一行并设置行高60
        Row row = sheet.createRow(1);
        row.setHeightInPoints(60);
        // 获取当前workbook的样式对象
        CellStyle style = workbook.createCellStyle();
        // 设置背景色(绿色)
        style.setFillBackgroundColor(IndexedColors.GREEN.getIndex());
        // 设置填充图案(厚水平带)填充图案默认为黑色()
        style.setFillPattern(FillPatternType.THICK_HORZ_BANDS);
        // 此时我设置填充图案(前景色为红色)
        style.setFillForegroundColor(IndexedColors.RED.getIndex());
        //创建行(1,1)并设置文本加样式
        Cell cell = row.createCell(1);
        cell.setCellValue("蚂蚁小哥");
        cell.setCellStyle(style);
        //创建流并写出文件关闭流
        OutputStream out = new FileOutputStream("D:\\test3.xlsx");
        workbook.write(out);
        out.close();
        workbook.close();
    }


点开查看详情:图案填充 FillPatternType 枚举类

java 复制代码
    常量名           汉译
    ALT_BARS:           	宽点
    BIG_SPOTS:          	大斑点
    BRICKS:             	砖状布局
    DIAMONDS:          	钻石
    FINE_DOTS:          	小细点
    LEAST_DOTS:         	最小点
    LESS_DOTS:          	少点
    NO_FILL:            	无背景
    SPARSE_DOTS:        	稀疏点
    SQUARES:            	正方形
    THICK_BACKWARD_DIAG:	厚厚的后向对角线
    THICK_FORWARD_DIAG: 	厚正面对角线
    THICK_HORZ_BANDS:   	厚水平带
    THICK_VERT_BANDS:   	厚垂直带
    THIN_BACKWARD_DIAG: 	薄后向对角线
    THIN_FORWARD_DIAG:  	细正对角线
    SOLID_FOREGROUND:   	实填

点开查看详情:背景颜色 IndexedColors 枚举类

java 复制代码
    常量名           汉译
    BLACK:              黑色
    BLACK1:             黑色1
    WHITE:              白色
    WHITE1:             白色1
    RED:                红色
    RED1:               红色1
    BRIGHT_GREEN:       亮绿色
    BRIGHT_GREEN1:      亮绿色1
    BLUE:               蓝色
    BLUE1:              蓝色1
    YELLOW:             黄色
    YELLOW1:            黄色1
    PINK:               粉红
    PINK1:              粉色1
    TURQUOISE:          青绿色
    TURQUOISE1:         青绿色1
    LEMON_CHIFFON:      柠檬雪纺
    LIGHT_TURQUOISE:    浅青绿色
    LIGHT_TURQUOISE1:   浅青绿色1
    GREEN:              绿色
    VIOLET:             紫罗兰
    TEAL:               蓝绿色
    MAROON:             栗色
    ROSE:               粉红色
    AQUA:               水绿色
    LIME:               石灰色
    GOLD:               金
    LAVENDER:           淡紫色
    BROWN:              棕色
    PLUM:               紫红色
    INDIGO:             靛蓝色
    TAN:                棕黄色
    ORCHID:             兰花色
    CORAL:              珊瑚色
    ROYAL_BLUE:         皇家蓝
    ORNFLOWER_BLUE:     矢车菊蓝
    ORANGE:             桔黄色的
    OLIVE_GREEN:        橄榄绿
    DARK_RED:           深红色
    DARK_BLUE:          深蓝色
    DARK_YELLOW:        深黄色
    DARK_GREEN:         深绿色
    DARK_TEAL:          深青色
    LIGHT_GREEN:        浅绿色
    LIGHT_YELLOW:       浅黄色
    LIGHT_ORANGE:       淡橙色
    LIGHT_BLUE:         浅蓝色
    LIGHT_CORNFLOWER_BLUE:浅矢车菊蓝
    PALE_BLUE:          淡蓝色
    SEA_GREEN:          海绿色
    BLUE_GREY:          蓝灰
    SKY_BLUE:           天空蓝色
    GREY_25_PERCENT:    灰25%
    GREY_40_PERCENT:    灰40%
    GREY_50_PERCENT:    灰50%
    GREY_80_PERCENT:    灰80%
    AUTOMATIC:          自然色

4:合并单元格

设置单元格的合并得掌握,其实也简单,只要使用addMergedRegion方法即可完成,下面是示例代码:
点开查看详情:合并单元格

java 复制代码
    public static void mergeCells() throws IOException {
        // 创建工作簿和创建Sheet0工作表
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet();
        // 设置单元格合并 0开始索引
        // 前2个参数设置开始行到结束行,
        // 后2个参数设置开始列到结束列;切记这和我们坐标轴不一样
        // CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
        sheet.addMergedRegion(new CellRangeAddress(1,1,1,5));
        // 创建行和列,此时坐标为 1,1;在Excel里就是第二行第二列
        Row row = sheet.createRow(1);
        Cell cell = row.createCell(1);
        cell.setCellValue("蚂蚁小哥");
        //创建流并写出文件关闭流
        OutputStream out = new FileOutputStream("D:\\test3.xlsx");
        workbook.write(out);
        out.close();
        workbook.close();
    }

5:字体样式

设置字体则通过工作簿先获取字体样式类,然后再对字体设置;字体样式类就使用 Font接口即可:
点开查看详情:设置字体样式

java 复制代码
    public static void fontStyle() throws IOException {
        // 创建工作簿和创建Sheet0工作表
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet();
        // 创建行
        Row row = sheet.createRow(1);
        // ## 创建字体类
        Font font = workbook.createFont();
        // ## 设置字体颜色
        font.setColor(IndexedColors.RED1.getIndex());
        // ## 设置字体 true粗体(默认) false细
        font.setBold(false);
        // ## 设置字体大小
        font.setFontHeightInPoints((short) 60);
        // ## 设置字体名 如楷体,微软雅黑....中文和英文表示都行
        font.setFontName("楷体");
        // ## 倾斜设置
        font.setItalic(true);
        // ## 设置删除线
        font.setStrikeout(true);
        // 创建样式类
        CellStyle cellStyle = workbook.createCellStyle();
        // 样式设置font样式
        cellStyle.setFont(font);
        // 创建列坐标为 1,1 就是Excel的第二行第二列的单元格设置信息
        Cell cell = row.createCell(1);
        cell.setCellValue("蚂蚁小哥_aBc_123");
        cell.setCellStyle(cellStyle);
        //创建流并写出文件关闭流
        OutputStream out = new FileOutputStream("D:\\test3.xlsx");
        workbook.write(out);
        out.close();
        workbook.close();
    }

6:快速构建单元格

样式的创建和单元格的创建其实每次设置样式都要创建对象,麻烦;但是ReginUtil(区域工具)CellUtil(样式工具)可以很快构建我们的样式,可以简写很多代码。示例代码:

CellUtil(样式工具): 可用于获取或创建单元格,并在创建单元格时设置给定的样式。

RegionUtil(区域工具): 对合并的单元格快速设置范围的边框、背景色和其它样式。
点开查看详情:快速构建单元格及样式

java 复制代码
    public static void fontStyle() throws IOException {
        // 创建工作簿和创建名称为"new sheet"的工作表
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("new sheet");
        // ========================================
        // 创建单元格坐标 1,1 ;在Excel为第二行第二列;并设置内容
        Cell cell = sheet.createRow(1).createCell(1);
        cell.setCellValue("蚂蚁小哥");
        // 创建单元格的范围 B2(1,1):E5(4,4) ; 说明,这个单元格以b2开始到e5结束
        CellRangeAddress region = CellRangeAddress.valueOf("B2:E5");
        // 上面的合并其实和 new CellRangeAddress(1,4,1,4) 一样的
        // 传递给合并单元格方法里
        sheet.addMergedRegion(region);
        //设置四边样式及颜色  但是得传递Sheet和CellRangeAddress
        RegionUtil.setBorderBottom(BorderStyle.MEDIUM_DASHED, region, sheet);
        RegionUtil.setBorderTop(BorderStyle.MEDIUM_DASHED, region, sheet);
        RegionUtil.setBorderLeft(BorderStyle.MEDIUM_DASH_DOT, region, sheet);
        RegionUtil.setBorderRight(BorderStyle.MEDIUM_DASHED, region, sheet);
        RegionUtil.setBottomBorderColor(IndexedColors.BLUE.getIndex(), region, sheet);
        RegionUtil.setTopBorderColor(IndexedColors.AQUA.getIndex(), region, sheet);
        RegionUtil.setLeftBorderColor(IndexedColors.RED.getIndex(), region, sheet);
        RegionUtil.setRightBorderColor(IndexedColors.AQUA.getIndex(), region, sheet);
        // ========================================
        // (创建单元格第2行;Excel里就是第二列)(创建单元格第3行;Excel里就是第三列)
        Row row = sheet.createRow(2);
        Row row2 = sheet.createRow(3);
        // 创建字体类并设置样式
        Font font = workbook.createFont();
        font.setColor(IndexedColors.RED1.getIndex());
        font.setStrikeout(true);
        // 创建样式类
        CellStyle style = workbook.createCellStyle();
        style.setFont(font);
        // 快捷创建单元格的两种方式
        CellUtil.createCell(row, 8, "Hello World!", style);
        Cell cell2 = CellUtil.createCell(row2, 8, "阿三大苏打");
        // 设置居中
        CellUtil.setAlignment(cell2, HorizontalAlignment.CENTER);
        // 创建流并写出文件关闭流
        OutputStream out = new FileOutputStream("D:\\test3.xlsx");
        workbook.write(out);
        out.close();
        workbook.close();
    }

7:图片样式的处理

在POI中要在单元格内设置一张图片的话则需要使用HSSFPatriarch(.xls格式)或XSSFDrawing(.xlsx格式)类。这两个类的上层接口都是Drawing,而Drawing接口及其子类是用来控制图片写入;图片位置的单元格位置或单元格内的偏移则使用ClientAnchor接口来指定;下面主要以看具体代码:
点开查看详情:单元格图片设置

java 复制代码
    public static void setCellImages() throws IOException {
        // 先获取图片的字节输出流
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        // 读取图片信息,并放入到带有缓存区的图片类中(这里图片可以设置流对象或者文件路径)
        BufferedImage read = ImageIO.read(new File("D:\\images\\tupian.png"));
        // 将图片写到字节输出流中
        ImageIO.write(read, "PNG", byteArrayOutputStream);
        //===========================================================================
        // 创建工作簿,并设置个默认的工作表
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet();
        //===========================================================================
        // 需求:组合一个3*13的单元格,并把图片设置平铺到这3*13的单元格中(单元格组合范围:B2:D14)
        // 先合并单元格
        sheet.addMergedRegion(CellRangeAddress.valueOf("B2:D14"));
        // 写入图片到Excel中
        // 构建图片写入的Drawing接口实现类对象
        XSSFDrawing patriarch = sheet.createDrawingPatriarch();
        // 指定图片的位置(重要)
        XSSFClientAnchor xssfClientAnchor = new XSSFClientAnchor(
                36000 * 5, 36000 * 5, -36000 * 5, -36000 * 5,
                1, 1, 4, 14);
        // 把图片写入到sheet指定位置
        int picture = workbook.addPicture(byteArrayOutputStream.toByteArray(),
                XSSFWorkbook.PICTURE_TYPE_PNG);
        patriarch.createPicture(xssfClientAnchor, picture);
        //创建流并写出文件关闭流
        OutputStream out = new FileOutputStream("D:\\test4.xlsx");
        workbook.write(out);
        out.close();
        workbook.close();
    }
"上面具体详细介绍:"
    Ⅰ:关于创建XSSFClientAnchor对象的 8 个int类型参数(4个为一组):
        col1,row1,col2,row2
            col1和row1(左上角多少行多少列)
                上面案例是左上角第二行第二列(确定了坐标1,1单元格左上角位置)
            col2和row2(右下角多少行多少列)
                上面案例是左上角第十五行第五列(确定了坐标14,4单元格左上角位置)
        dx1,dy1,dx2,dy2
            图片偏移位置;使用"英式公制"单位,36000=1毫米
            上面设置的 36000*5 代表5毫米。
    Ⅱ:枚举XSSFWorkbook.PICTURE_TYPE_PNG类型
        需要设置类型时使用,通过枚举来获取图片的格式
        场景如:
            PICTURE_TYPE_GIF
            PICTURE_TYPE_TIFF
            PICTURE_TYPE_PNG
            PICTURE_TYPE_JPEG...

(五):Excel百万数据导出

我们知道Excel可以分为早期的97-2003版本(使用POI的HSSF对象操作)和2007版本(使用POI的XSSF操作),两者对百万数据的支持如下:

  • Excel(97-2003):在POI中使用HSSF对象时,97-2003最多只允许存储65536条数据,一般用来处理较少的数据量。这时对于百万级别数据,Excel肯定容纳不了。
  • Excel(2007):POI中使用XSSF对象时,它可以直接支持2007以上版本,因为它采用ooxml格式。这时excel可以支持1048576条数据,单个sheet表就支持近百万条数据。但实际运行时还可能存在问题,原因是执行POI报表所产生的行对象,单元格对象,字体对象,它们都不会销毁,这就导致OOM的风险。

<math xmlns="http://www.w3.org/1998/Math/MathML"> 如何解决百万数据导出内存溢出问题: \color{#f00}{如何解决百万数据导出内存溢出问题:} </math>如何解决百万数据导出内存溢出问题:

使用SXSSFWorkbook对象,它是Apache POI中针对大数据量Excel导出而设计的一种流式工作簿实现。它能够有效地解决大数据导出时可能出现的内存溢出问题,具体有以下几点原因:

  1. 基于临时文件存储数据:SXSSFWorkbook在内部使用一种临时文件来存储数据,而不是将所有数据都存储在内存中。这样就可以避免在处理大量数据时耗尽内存空间导致内存溢出。
  2. 限制内存使用:SXSSFWorkbook有一个内存敏感的阈值,当工作簿占用的内存达到设定的阈值时,会自动将数据写入到临时文件中,释放内存空间。这样可以保持内存的稳定使用,避免过度占用内存。
  3. 基于XSSF实现:SXSSFWorkbook是基于XSSF(XML Spreadsheet Format)实现的,因此能够支持XLSX格式的Excel文件,同时提供了与XSSFWorkbook类似的API接口,方便使用和迁移。
  4. 优化写入性能:SXSSFWorkbook在数据写入时进行了优化处理,采用了一种缓冲机制来提高写入性能,同时避免造成内存压力过大。
  5. 自动清理临时文件:在SXSSFWorkbook关闭时,会自动清理临时文件,确保不会留下无用的临时文件占用磁盘空间。

我们可以设置 "内存敏感的阈值" ,这个阈值用来控制内存使用的情况。在SXSSFWorkbook中,默认的内存敏感的阈值是100行。这意味着当工作簿占用的内存达到100行数据时,SXSSFWorkbook会自动将数据写入到临时文件中,以释放内存空间。

也可以使用默认的内存敏感阈值:new SXSSFWorkbook(SXSSFWorkbook.DEFAULT_WINDOW_SIZE)

<math xmlns="http://www.w3.org/1998/Math/MathML"> 导出百万 E x c e l 思路: \color{#f00}{导出百万Excel思路:} </math>导出百万Excel思路:

HSSFWorkbook导出数据有限,而XSSFWorkbook虽然可以导每个Sheet一百多万条数据,但是这会导致内存溢出;导出百万Excel数据只能使用SXSSFWorkBook了。

假设现在导出200万条数据,那么我每个Sheet工作表存放100万条数据(实则最多可存放1048576条);这样两个Sheet就可以装下200万条数据了;但是需要导出的十万或百万数据从哪来呢?在日常开发中数据来自数据库查询,可千万不能一次性把所有数据都查出来了,这样内存会爆的,我们可以通过分页每次查询部分数据,再放到SXSSFWorkbook创建的Excel中;还需要注意的是:导出大量数据记录则不能使用模板和使用太多样式,因为这些样式的创建也都是占用内存的,防止内存溢出。下面就一起来操作吧:

java 复制代码
    public static void main(String[] args) throws IOException {
        // 开始时间
        long startTime = System.currentTimeMillis();
        // 创建工作薄存放百万数据;要替换成XSSFWorkbook可能会报内存溢出且执行速度很慢
        // SXSSFWorkbook构造器可以设置内存阈值,就是处理完多少行后释放内存,并进行下一轮的操作
        SXSSFWorkbook workbook = new SXSSFWorkbook();
        // 创建工作表
        Sheet sheet = null;
        // 分页模拟查询数据库数据
        int pageSize = 1;   // 当前页码
        int num = 0;        // 记录了处理的总行数
        while (true) {
            // 模拟分页查询数据库数据;查询完本轮数据完页码每次累加;每次查询一万条(具体查询以数据大小定义)
            List<Student> studentList = findPage(pageSize++, 10000);
            // 查不到数据则退出循环
            if (studentList.isEmpty()) {
                break;
            }
            // 计算共有多少工作表(总记录)
            if (num % 1000000 == 0) {
                String sheetName = "第" + ((num / 1000000) + 1) + "个工作表";
                System.out.println("创建:" + sheetName);
                sheet = workbook.createSheet(sheetName);
                // 添加每个工作表标题
                String[] titles = {"ID", "姓名", "手机号", "地址"};
                Row rowTitle = sheet.createRow(0);
                for (int i = 0; i < titles.length; i++) {
                    rowTitle.createCell(i).setCellValue(titles[i]);
                }
            }
            // 循环遍历数据
            for (Student student : studentList) {
                // 每次循环后都创建一条记录表信息;获取当前最后一行并且+1,代表获取新行
                Row row = sheet.createRow(sheet.getLastRowNum() + 1);
                // 创建记录列
                row.createCell(0).setCellValue(student.getId());
                row.createCell(1).setCellValue(student.getName());
                row.createCell(2).setCellValue(student.getPhone());
                row.createCell(3).setCellValue(student.getProvince());
                // 记录处理行数++
                num++;
            }
        }
        //创建流并写出文件关闭流
        OutputStream out = Files.newOutputStream(Paths.get("D:\\test4.xlsx"));
        workbook.write(out);
        out.close();
        workbook.close();
        // 结束时间
        System.out.println("总用时:" + (System.currentTimeMillis() - startTime) / 1000 + " 秒");
    }
    
     /***
     * 模拟分页方法,传入参数后模拟去查询数据库并返回分页后的数据(这个方法不用管,能用就行)
     * @param currentPage 当前页
     * @param numPages 每页数
     */
    public static List<Student> findPage(int currentPage, int numPages) {
        // 总记录数为200万条,不管怎么分页都是200万
        int total = 2000000;
        ArrayList<Student> result = new ArrayList<>();
        currentPage = currentPage == 0 ? currentPage + 1 : currentPage;
        // 计算起始ID
        int startID = currentPage * numPages - numPages;
        // 若超出则返回空对象,未超出则返回数据
        if (currentPage * numPages > total && currentPage > 1) {
            return result;
        } else {
            startID = startID + 1;
            int s = 0;
            if (currentPage <= 1 && currentPage * numPages > total) {
                s = total;
            } else {
                s = currentPage * numPages;
            }
            Random random = new Random();
            for (int i = startID; i <= s; i++) {
                int anInt = random.nextInt(6);
                Student student = new Student(i, "测试" + i, "176" + (anInt++) + "13"
                        + (anInt++) + "3" + (anInt++) + "8" + (anInt++), "安徽");
                result.add(student);
            }
        }
        return result;
    }

    // 自定义类
    public class Student {
        private Integer id;         // 主键ID
        private String name;        // 姓名
        private String phone;       // 电话
        private String province;    // 省份
        // 省略构造器,get,set方法
    }

(六):Excel百万数据导入

百万级的数据导入场景其实并不多,但若真的遇到了,使用普通的HSSF或者XSSF的工作薄一行行读取的话,那么大数据量会报OOM内存溢出;所以我使用如下的方式来读取我那百万级的数据;处理思路就是加载Excel并读取,读取其实是按行读取的,把读取的指定数据存放到集合中,当读取的数据超过最大容量,则会通过回调的方式处理完本次数据,处理完成以后继续读取Excel并放到集合中。
点开查看详情:ExcelParse解析器

java 复制代码
/***
 * Excel解析器(用来读取Excel数据)
 * 下面这段代码是固定的,可以复制使用,但需要注意传入的是,不同的Excel可能有着不同的handler处理方式
 * @author Anhui AntLaddie (掘金蚂蚁小哥)
 * @version 1.0
 **/
public class ExcelParse {
    /***
     * Excel解析工具方法
     * @param inputFilePath 文件输入路径信息
     * @param handler 处理器(不同的Excel有着不同的处理方式)
     */
    public void parse(String inputFilePath,
                      XSSFBSheetHandler.SheetContentsHandler handler) {
        // 打开一个OPCPackage对象;以只读方式打开指定路径下的Excel文件
        try (OPCPackage opcPackage = OPCPackage.open(inputFilePath, PackageAccess.READ)) {
            // 使用 XSSFReader 对象来读取 OPCPackage 中的内容
            XSSFReader reader = new XSSFReader(opcPackage);
            // 获取 Excel 文件中的共享字符串表
            SharedStrings stringsTable = reader.getSharedStringsTable();
            // 获取 Excel 文件中的样式表信息
            StylesTable stylesTable = reader.getStylesTable();
            // 创建SAX解析器,然后配置为支持命名空间,并最终获取一个能够解析XML文档的XMLReader对象。
            // 注:org.xml.sax.XMLReader;下的类
            SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
            saxParserFactory.setNamespaceAware(true);
            XMLReader xmlReader = saxParserFactory.newSAXParser().getXMLReader();
            // 设置 XMLReader 的内容处理器,用于处理 Excel 表格中的数据。
            //      stylesTable:表示Excel文件中的样式表,(单元格的格式、字体、颜色等样式信息)
            //      stringsTable:代表Excel文件中的共享字符串表,其中存储了单元格中使用的字符串。
            //      handler:表示自定义的处理器对象,用于对Excel表格中的每行每列数据进行处理。
            //      false:则表示不忽略空白单元格(若设置true代表忽略空白单元格字符串)
            xmlReader.setContentHandler(new XSSFSheetXMLHandler(stylesTable,
                    stringsTable, handler, false));
            // 循环遍历,处理Excel中的每个工作表数据
            // XSSFReader中获取Excel文件的工作表数据,
            //并创建一个XSSFReader.SheetIterator的实例来迭代处理各个工作表
            XSSFReader.SheetIterator sheetIterator 
                    = (XSSFReader.SheetIterator) reader.getSheetsData();
            while (sheetIterator.hasNext()) {
                InputStream sheetStream = sheetIterator.next();
                // 注:org.xml.sax.InputSource;下的类
                InputSource sheetSource = new InputSource(sheetStream);
                try {
                    xmlReader.parse(sheetSource);
                } finally {
                    sheetStream.close();
                }
            }
        } catch (Exception e) {
            // 这里请改成log日志方式打印
            System.out.println("Excel解析处理异常:" + e.getMessage());
        }
    }
}
java 复制代码
"自定义SheetHandler的Excel处理器(代码基本无需改动)"

/***
 * 自定义sheet基于Sax的解析处理器
 * @author Anhui AntLaddie(掘金蚂蚁小哥)
 * @version 1.0
 **/
public class SheetHandler implements XSSFBSheetHandler.SheetContentsHandler {
    // 集合最大存放容量
    public static int MAXIMUM_ROWS = 100000;
    // 创建一个集合数组,读取的数据全部往集合内存放
    private List<Map<String, String>> dataLists = new ArrayList<>();
    // 每行数据信息
    private Map<String, String> cellData = null;
    // 单元格对应关系(就是单元格的标题和实体属性名的对应关系)
    private Map<String, String> cellCorrespondence = new HashMap<>();
    // 读取的数据处理回调函数(当返回true代表成功处理回调;当返回false则停止后续操作)
    private final Function<List<Map<String, String>>, Boolean> readDataProcessFun;
    /***
     * 构造函数
     * @param cellCorrespondence 单元格与对象属性对应关系
     * @param readDataProcessFun 读取的数据回调函数
     */
    public SheetHandler(Map<String, String> cellCorrespondence,
                        Function<List<Map<String, String>>, Boolean> readDataProcessFun) {
        this.cellCorrespondence = cellCorrespondence;
        this.readDataProcessFun = readDataProcessFun;
    }
    /***
     * 当开始解析某一行的时候触发
     * @param rowNum 当前处理的行号
     */
    @Override
    public void startRow(int rowNum) {
        // 若等于0则代表是标题行,后面代码不在处理,反之为数据行需要处理
        if (rowNum == 0) {
            cellData = null;
        } else {
            cellData = new HashMap<>();
        }
    }
    /***
     * 当结束解析某一行的时候出发
     * @param rowNum 当前处理的行号
     */
    @Override
    public void endRow(int rowNum) {
        // 当前行读取完成后把当前行数据添加到集合中
        if (cellData != null && !cellData.isEmpty()) {
            dataLists.add(cellData);
        }
        // 添加完成后将当前行数据指定为null,继续下一行的读取
        cellData = null;
        // 校验当前存储的Excel行记录是否超过指定的最大容量
        if (dataLists.size() >= MAXIMUM_ROWS) {
            Boolean result = readDataProcessFun.apply(dataLists);
            // 若result为true则代表当前一批数据处理完成,进行下一批数据的读取
            // 若result为false则代表当前一批数据处理完成,强行停止Excel后续的操作
            if (result) {
                dataLists = new ArrayList<>();
            } else {
                throw new RuntimeException("导入Excel文件数据失败,手动干预停止!");
            }
        }
    }
    /***
     * 对当前处理的行里的每一个单元格进行处理
     * @param cellReference 单元格名称,如(A12,C4,D166)这种单元格名称
     * @param formattedValue 单元格内的数据信息
     * @param comment 单元格的批注信息
     */
    @Override
    public void cell(String cellReference, String formattedValue, XSSFComment comment) {
        // 非标题数据则进行处理
        if (cellData != null) {
            // 剔除单元格名称的后面数值
            String cellName = cellReference.replaceAll("\\d", "");
            // 数据添加
            if (cellCorrespondence.containsKey(cellName)) {
                cellData.put(cellCorrespondence.get(cellName), formattedValue);
            }
        }
    }
    /***
     * 当本页工作表写完后则强行刷出一次操作,将剩余的集合数据行处理掉
     */
    @Override
    public void endSheet() {
        if (!dataLists.isEmpty()) {
            if (readDataProcessFun.apply(dataLists)) {
                dataLists = new ArrayList<>();
            } else {
                throw new RuntimeException("导入Excel文件数据失败,手动干预停止!");
            }
        }
    }
    /***
     * 超链接单元格(一般用不上)
     */
    @Override
    public void hyperlinkCell(String cellReference, String formattedValue,
                              String url, String toolTip, XSSFComment comment) {}
}
java 复制代码
/***
     * 调用测试(我这边电脑200w条数据18-20秒)
     */
    public static void main(String[] args) {
        // 开始时间
        long startTime = System.currentTimeMillis();
        HashMap<String, String> maps = new HashMap<>();
        maps.put("A", "id");
        maps.put("B", "name");
        maps.put("C", "phone");
        maps.put("D", "province");
        // 创建回调方式处理
        Function<List<Map<String, String>>, Boolean> getNameFunction = e -> {
            // 这里将返回每次读取的数据,在这里可以做业务处理,以及存放数据库
            // 返回true代表本批次数据处理完成,返回false则强行终止后面的读取操作
            // 读取的数据如下方式,上面的map关系需要对应好单元格
            // [{province=安徽, phone=17631343586, name=测试1999998, id=1999998},
            // {province=安徽, phone=17631343586, name=测试1999999, id=1999999},
            // {province=安徽, phone=17641353687, name=测试2000000, id=2000000}......]
            return true;
        };
        SheetHandler objectSheetHandler = new SheetHandler(maps, getNameFunction);
        new ExcelParse().parse("E:\\test4.zip", objectSheetHandler);
        System.out.println("执行完成");
        System.out.println("总用时:" + (System.currentTimeMillis() - startTime) / 1000 + " 秒");
    }

三:拥抱便捷开发EasyExcel

对于阿里巴巴的EasyExcel的工具类还是比较推荐的,官方有着详细的代码示例以及介绍;EasyExcel它是针对Apache POI技术的封装和优化,主要解决了POI技术的耗内存问题,并且提供了较好的API使用。不需要大量的代码就可以实现excel的操作功能。

关于Easyexcel | Easy Excel (alibaba.com)官方文档

EasyExcel全面教程快速上手-也是比较推荐的一篇博客

Maven导入EasyExcel的pom坐标依赖信息(只需要导入这个坐标依赖即可)

相关推荐
不务专业的程序员--阿飞5 分钟前
【SQL 如何解锁递归】
java·数据库·sql
嘵奇11 分钟前
Spring Boot拦截器详解:原理、实现与应用场景
java·spring boot·后端
八股文领域大手子12 分钟前
Java死锁排查:线上救火实战指南
java·开发语言·面试
jackson凌19 分钟前
【Java学习笔记】finalize方法
java·笔记·学习
fanTuanye22 分钟前
redis 缓存穿透,缓存击穿,缓存雪崩
java·redis·缓存
神秘的t39 分钟前
Spring Web MVC————入门(2)
java·spring·mvc
开开心心就好1 小时前
高效全能PDF工具,支持OCR识别
java·前端·python·pdf·ocr·maven·jetty
冷心笑看丽美人1 小时前
Spring MVC数据绑定和响应 你了解多少?
java·spring·mvc
XQ丶YTY1 小时前
大二java第一面小厂(挂)
java·开发语言·笔记·学习·面试
一零贰肆1 小时前
深入理解SpringBoot中的SpringCache缓存技术
java·springboot·springcache·缓存技术