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文件之间的关系。

'组件'                   '操作格式'                '依赖'              '说明'
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(只可以操作XLSPOIFS                   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:        PPTPPTX        poi-scratchpad 和 poi-ooxml
                        SL代码在核心POIjar中,但是实现的是poi-scratchpad和poi-ooxml
    Common SS:        XLSXLSX                 poi-ooxml
                        WorkbookFactory和其它实现都需要poi-ooxml,而不仅仅是核心po
    OpenXML4J:        OOXML         poi-ooxml和poi-ooxml-lite或poi-ooxml-full

m a v e n 关于 P O I 操作的整套依赖 \color{#f00}{maven关于POI操作的整套依赖} maven关于POI操作的整套依赖

<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列。

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

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

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

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参数。下面就来看看不同类型的单元格内容如何设置:

	/***
     * 创建多种类型单元格的文件
     */
    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();
    }
</code></pre> </details>

image.png

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

/*** 测试基本的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();
    }

image.png

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

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

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 方法来对单元格设置对齐方式:

	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();
    }

image.png

2:单元格边框及颜色

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

	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();
    }
</code></pre> </details>

![image.png](https://img-blog.csdnimg.cn/img_convert/59d8dfaa3c611d2da0684418a48b8820.png)

### 3:单元格颜色填充
&emsp;&emsp;有时候我们会设置单元格的前景色以及背景色,甚者会对单元格填充图案,这时候我们就会用到样式类里面的填充方法;关于枚举类:[**FillPatternType(图案背景)**](https://poi.apache.org/apidocs/dev/org/apache/poi/ss/usermodel/FillPatternType.html)[**IndexedColors(背景色)**](https://poi.apache.org/apidocs/dev/org/apache/poi/ss/usermodel/IndexedColors.html) 具体看代码:

<details> <summary>点开查看详情:设置单元格前景色和背景色</summary> <pre><code class="language-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();
    }

image.png
图案填充 FillPatternType 枚举类

常量名           汉译
    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 枚举类

    常量名           汉译
    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方法即可完成,下面是示例代码:

	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();
    }

image.png

5:字体样式

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

	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();
    }

image.png

6:快速构建单元格

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

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

  RegionUtil(区域工具): 对合并的单元格快速设置范围的边框、背景色和其它样式。

	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();
    }

image.png

7:图片样式的处理

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

	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对象的 8int类型参数(4个为一组):
        col1,row1,col2,row2
            col1和row1(左上角多少行多少列)
                上面案例是左上角第二行第二列(确定了坐标11单元格左上角位置)
            col2和row2(右下角多少行多少列)
                上面案例是左上角第十五行第五列(确定了坐标144单元格左上角位置)
        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...

image.png

(五):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的风险。

如何解决百万数据导出内存溢出问题: \color{#f00}{如何解决百万数据导出内存溢出问题:} 如何解决百万数据导出内存溢出问题:

  使用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)

导出百万 E x c e l 思路: \color{#f00}{导出百万Excel思路:} 导出百万Excel思路:

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

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

    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并放到集合中。

示例代码: \color{#f00}{示例代码:} 示例代码:

/***
 * Excel解析器(用来读取Excel数据)
 * 下面这段代码是固定的,可以复制使用,但需要注意传入的是,不同的Excel可能有着不同的handler处理方式
 * @author Anhui AntLaddie <a href="https://juejin.cn/user/4092805620185316">(掘金蚂蚁小哥)</a>
 * @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());
        }
    }
}
"自定义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) {}
}

/***
     * 调用测试(我这边电脑200w条数据18-20秒)
     */
    public static void main(String[] args) {
        // 开始时间
        long startTime = System.currentTimeMillis();
        // 这个Map是对应单元格,如A单元格(第一列第一行)对应id字段
        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 + " 秒");
    }

三:关于Excel操作可能发生的问题汇总

问题一: \color{#f00}{问题一:} 问题一:

image.png
  解决:若执行Excel导入导出代码时出现如下情况则需要把POI导入的几个版本降到5.0.0及以下。
  
问题二: \color{#f00}{问题二:} 问题二:
 org.apache.xmlbeans.XmlOptions.setUseDefaultNamespace(Z)Lorg/apache/xmlbeans/XmlOptions;
image.png
  解决:出现这种报错则需要查看自己pom.xml文件里的xmlbeans坐标是否冲突,一般来说把自己导入的xmlbeans坐标注释就行,或者是排除其它坐标内引用xmlbeans的坐标,使用exclusion标签;因为默认poi-ooxml坐标自带xmlbeans坐标,比如poi-ooxml 5.25版本就自带xmlbeans 5.20版本。

四:拥抱便捷开发EasyExcel

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

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

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

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

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java可以使用EasyExcel组件来导出数据到Excel表格EasyExcel是阿里巴巴开源的一个优秀的Java解析和生成Excel的框架。相对于其他框架如Apache POI和JXL,EasyExcel具有更低的内存消耗,能够处理大型Excel文件而不会导致内存溢出的问题。EasyExcel提供了简单易用的API,使得导出Excel变得简单方便。你可以通过访问EasyExcel的官方网站(https://easyexcel.opensource.alibaba.com)或者GitHub地址(https://github.com/alibaba/easyexcel)来了解更多关于EasyExcel的详细信息和使用方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java使用easyexcel导出数据导Excel表格](https://blog.csdn.net/weixin_45536587/article/details/124751757)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Java导出Excel文件合集(easyExcel)](https://blog.csdn.net/weixin_42555014/article/details/131853805)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值