首页 > 编程语言 >【JAVA开发笔记】实战演练,如何用EasyExcel导出表格,并且自定义合并单元格

【JAVA开发笔记】实战演练,如何用EasyExcel导出表格,并且自定义合并单元格

时间:2024-06-15 22:58:44浏览次数:17  
标签:row1 row2 row3 JAVA 自定义 EasyExcel resultList TestEntity build

目录

1. 前言

2. EasyExcel简介

3. EasyExcel简单导出案例讲解

3.1 EasyExcel依赖引入

3.2 测试类创建

3.3 Excel导出实现

4. EasyExcel合并单元案例讲解

4.1 实现自定义合并策略

4.2 使用自定义合并策略

5. 总结


1. 前言

项目上,需将一个列表数据导出Excel表格,并将指定列相同数据自动合并单元格,琢磨学习了下easyexcel实现效果如下:

如上图所示,指定A、B两列相同行自动合并。

2. EasyExcel简介

  • EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。
  • EasyExcel相比其他Excel解析框架(Apache poi和jxl),拥有更好的内存消耗管理算法。特别是对07版Excel的解决,EasyExcel重写了底层解析逻辑,一个3M的Excel解析只需要几M内存,但是用poi解析可能需要100M左右的内存。EasyExcel提高了读取性能,64M内存20秒读取75M的Excel,还有更快的极速模式,但是消耗的内存会更多一些。
  • EasyExcel支持自定义策略合并单元格,可以方便快捷填充数据到模板中,有活跃的中文社区支持,完善的测试用例可以覆盖大部分业务场景的使用。

3. EasyExcel简单导出案例讲解

 本章节实现效果如下:

3.1 EasyExcel依赖引入

注意文中还需引入Lombok注解

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.2.1</version>
        </dependency>

3.2 测试类创建

@ContentRowHeight(50) //内容行高

@HeadRowHeight(15) //表头行高

@ColumnWidth(20) //列宽度

@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER) // 内容样式,本处设置是水平和垂直居中

@ExcelProperty("国家/地区") //表头信息

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.ContentStyle;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.alibaba.excel.enums.poi.HorizontalAlignmentEnum;
import com.alibaba.excel.enums.poi.VerticalAlignmentEnum;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;


/**
 * @Author: lxy
 * @CreateTime: 2024-06-11
 * @Description: 测试类
 */
@Getter
@Setter
@Builder
@EqualsAndHashCode
@HeadRowHeight(50) // 表头行高
@ContentRowHeight(15) // 内容行高
@ColumnWidth(20) // 列宽度
public class TestEntity {


    @ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER) // 内容样式
    @ExcelProperty("国家/地区")
    private String row1;
    
    @ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER) // 内容样式
    @ExcelProperty("省份/州/自治区/特别行政区")
    private String row2;
    
    @ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER) // 内容样式
    @ExcelProperty("城市/县/市辖区")
    private String row3;
}

3.3 Excel导出实现

使用EasyExcel导出简单Excel代码示例如下:

    @Test
    public void simplyWriteExcel() {
        // 数据就初始化
        List<TestEntity> resultList = new ArrayList<>();
        resultList.add(TestEntity.builder().row1("中国").row2("广东省").row3("深圳市").build());
        resultList.add(TestEntity.builder().row1("中国").row2("广东省").row3("广州市").build());
        resultList.add(TestEntity.builder().row1("中国").row2("新疆维吾尔自治区").row3("乌鲁木齐市").build());
        resultList.add(TestEntity.builder().row1("中国").row2("新疆维吾尔自治区").row3("喀什地区").build());
        resultList.add(TestEntity.builder().row1("中国").row2("香港特别行政区").row3("香港岛").build());
        resultList.add(TestEntity.builder().row1("中国").row2("香港特别行政区").row3("九龙半岛").build());
        resultList.add(TestEntity.builder().row1("美国").row2("加利福尼亚州").row3("洛杉矶市").build());
        resultList.add(TestEntity.builder().row1("美国").row2("加利福尼亚州").row3("旧金山县").build());
        resultList.add(TestEntity.builder().row1("美国").row2("德克萨斯州").row3("休斯敦市").build());
        resultList.add(TestEntity.builder().row1("美国").row2("德克萨斯州").row3("达拉斯县").build());
        resultList.add(TestEntity.builder().row1("美国").row2("纽约州").row3("纽约市").build());
        resultList.add(TestEntity.builder().row1("美国").row2("纽约州").row3("布法罗县").build());
        // 设置文件名称
        String fileName = "C:\\Users\\Lixy\\Desktop\\test01.xlsx";
        // 1、指定写出文件名称以及用哪个class去写
        // 2、设置文件流自动关闭
        // 3、输出写出Excel的sheet名称(自定义)
        // 4、指定写出的数据
        EasyExcel.write(fileName, TestEntity.class)
                .autoCloseStream(Boolean.TRUE)
                .sheet("Sheet1").doWrite(resultList);
    }

4. EasyExcel合并单元案例讲解

注:EasyExcel依赖和测试类,与章节3保持一致,本章节不再做讲解

 本章节实现效果如下:

4.1 实现自定义合并策略

CellWriteHandler 是 EasyExcel 中的一个接口,它允许开发者在写入单元格时执行自定义逻辑,如设置单元格样式、合并单元格等。

下面是一个的 CellWriteHandler 示例,实现了如何判断上下行数据相同,并对数据进行合并单元格:

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @Author: lxy
 * @CreateTime: 2024-06-12
 * @Description: EasyExcel单元格合并处理器
 */
public class ExcelMergeHandler implements CellWriteHandler {

    private int[] mergeColumnIndex;
    private int mergeRowIndex;

    public ExcelMergeHandler() {
    }

    /**
     * 构造函数
     *
     * @param mergeRowIndex     合并开始的行索引
     * @param mergeColumnIndex  要合并的列索引数组
     */
    public ExcelMergeHandler(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }


    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        // 当前行索引
        int curRowIndex = cell.getRowIndex();
        // 当前列索引
        int curColIndex = cell.getColumnIndex();
        // 如果当前行大于合并开始行
        if (curRowIndex > mergeRowIndex) {
            // 当前列在需要合并的列中
            for (int columnIndex : mergeColumnIndex) {
                if (curColIndex == columnIndex) {
                    // 进行合并操作
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }


    /**
     * 当前单元格向上合并
     *
     * @param writeSheetHolder 当前工作表持有者
     * @param cell             当前单元格
     * @param curRowIndex      当前行索引
     * @param curColIndex      当前列索引
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        // 获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并
        Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        // 获取前一个单元格的数据
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();

        // 判断当前单元格和前一个单元格的数据以及主键是否相同
        if (curData.equals(preData)) {
            // 获取工作表
            Sheet sheet = writeSheetHolder.getSheet();
            // 获取已合并的区域
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            boolean isMerged = false;
            // 检查前一个单元格是否已经被合并
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // 如果前一个单元格未被合并,则新增合并区域
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }


    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {

    }
}

4.2 使用自定义合并策略

要使用 CellWriteHandler,通常需要实现其定义的方法,并在创建 ExcelWriter 时通过 registerWriteHandler 方法将其注册到 EasyExcel 的上下文中,这样,当 EasyExcel 写入单元格时,就会调用这些自定义的处理器方法。

在创建 ExcelWriter 并写入数据时,可以如下注册这个处理器:

    @Test
    public void writeExcel() {
        // 需要合并的列
        int[] mergeColumnIndex = {0,1};
        // 需要从第几行开始合并
        int mergeRowIndex = 1;
        // 数据就初始化
        List<TestEntity> resultList = new ArrayList<>();
        resultList.add(TestEntity.builder().row1("中国").row2("广东省").row3("深圳市").build());
        resultList.add(TestEntity.builder().row1("中国").row2("广东省").row3("广州市").build());
        resultList.add(TestEntity.builder().row1("中国").row2("新疆维吾尔自治区").row3("乌鲁木齐市").build());
        resultList.add(TestEntity.builder().row1("中国").row2("新疆维吾尔自治区").row3("喀什地区").build());
        resultList.add(TestEntity.builder().row1("中国").row2("香港特别行政区").row3("香港岛").build());
        resultList.add(TestEntity.builder().row1("中国").row2("香港特别行政区").row3("九龙半岛").build());
        resultList.add(TestEntity.builder().row1("美国").row2("加利福尼亚州").row3("洛杉矶市").build());
        resultList.add(TestEntity.builder().row1("美国").row2("加利福尼亚州").row3("旧金山县").build());
        resultList.add(TestEntity.builder().row1("美国").row2("德克萨斯州").row3("休斯敦市").build());
        resultList.add(TestEntity.builder().row1("美国").row2("德克萨斯州").row3("达拉斯县").build());
        resultList.add(TestEntity.builder().row1("美国").row2("纽约州").row3("纽约市").build());
        resultList.add(TestEntity.builder().row1("美国").row2("纽约州").row3("布法罗县").build());
        // 设置文件名称
        String fileName = "C:\\Users\\Lixy\\Desktop\\test02.xlsx";
        // 1、指定写出文件名称以及用哪个class去写
        // 2、设置文件流自动关闭
        // 3、设置自定义的写入处理逻辑,注册ExcelMergeHandler示例
        // 4、输出写出Excel的sheet名称(自定义)
        // 5、指定写出的数据
        EasyExcel.write(fileName, TestEntity.class)
                .autoCloseStream(Boolean.TRUE)
                .registerWriteHandler(new ExcelMergeHandler(mergeRowIndex, mergeColumnIndex))
                .sheet("Sheet1").doWrite(resultList);
    }

5. 总结

EasyExcel功能灵活强大,可以根据自身业务场景去自定义样式,也可以使用通过模板填充功能实现导出国际化语言等复杂功能。

标签:row1,row2,row3,JAVA,自定义,EasyExcel,resultList,TestEntity,build
From: https://blog.csdn.net/weixin_47698656/article/details/139626118

相关文章

  • 机器视觉入门学习:YOLOV5自定义数据集部署、网络详解、损失函数(学习笔记)
     前言源码学习资源:YOLOV5预处理和后处理,源码详细分析-CSDN博客网络学习资源:YOLOv5网络详解_yolov5网络结构详解-CSDN博客YOLOv5-v6.0学习笔记_yolov5的置信度损失公式-CSDN博客 本文为个人学习,整合各路大佬的资料进行V5-6.0版本的网络分析,在开始学习之前最好先去学习YOL......
  • JavaScript 的原型链机制
    JavaScript的原型链机制是其继承模型的核心概念,它允许对象通过原型链访问和继承其他对象的属性和方法。原型链机制是实现JavaScript面向对象编程的基础。1.原型和原型链的基本概念原型对象(prototype):每个JavaScript对象(除了null)都有一个与之关联的对象,这个对象就......
  • Java程序设计的精髓:构建稳健的异常处理体系
    在Java的世界里,异常处理是确保程序稳定性和健壮性的关键一环。一个良好的异常处理机制不仅能够提升用户体验,还能在出现问题时保护应用程序不受损害。本文将深入探讨Java中的异常处理机制,并通过实例和图解来展示如何构建一个稳健的异常处理体系。异常处理基础在Java中,异常(Exce......
  • 最全Java面试题及答案整理(2024最新版)
    很多Java工程师的技术不错,但是一面试就头疼,10次面试9次都是被刷,过的那次还是去了家不知名的小公司。问题就在于:面试有技巧,而你不会把自己的能力表达给面试官。应届生:你该如何准备简历,面试项目和面试说辞?Spring底层逻辑是什么?1-3年经验的程序员:面试中你该讲哪些值钱......
  • Java并行世界的钥匙:一文带你了解Java ForkJoin并行框架
    Fork/Join框架是Java7引入的一个并行计算框架,主要用于处理可以通过递归分解成更细小的任务的场景。其基本结构和工作流程可以从以下几个方面进行详细解析:核心类ForkJoinPool:这是一个线程池类,用于执行ForkJoinTask任务。ForkJoinWorkerThread:这是执行任务的具体线程实体......
  • Java新纪元:深入探索Java 17的新特性与最佳实践
    一、主要新特性Java17作为Java的最新长期支持(LTS)版本,带来了许多新特性和改进。以下是对Java17新特性的详细探索,结合图文说明。密封类(SealedClasses)Java17引入了密封类,这是一种新的类定义方式,可以限制哪些其他类可以继承一个密封类。密封类的引入旨在解决Java中继承......
  • 【秋招突围】2024届秋招笔试-小红书笔试题-第一套-三语言题解(Java/Cpp/Python)
    ......
  • JavaWeb课程设计/期末大作业-电影网站+源代码+文档说明+数据库sql
    文章目录源码下载地址项目介绍项目功能界面预览项目备注源码下载地址源码下载地址点击这里下载代码项目介绍项目功能界面预览项目备注1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!2、本项目适合计算机相关专业(如计科......
  • 利用Elasticsearch提升Java应用的搜索能力
    引言:在数据驱动的时代,能够快速地处理和分析大量数据变得至关重要。Elasticsearch不仅提供全文搜索功能,还支持复杂的数据分析,是现代应用中不可或缺的工具之一。什么是Elasticsearch?Elasticsearch是一个高度可扩展的开源全文搜索和分析引擎。它允许你以近实时的方式存储、搜索......
  • Java与服务网格(Service Mesh):构建高效微服务架构
    在微服务架构成为企业开发标准的今天,如何有效地管理众多微服务之间复杂的通信成为了一个挑战。服务网格作为一种解决方案,它通过提供一个专门的基础设施层来处理服务间通信,从而使得应用开发更加专注于业务逻辑而非通信细节。本文将介绍服务网格的基本概念,探讨其在Java环境中的应......