Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >使用EasyExcel导出表格时合并单元格

使用EasyExcel导出表格时合并单元格

作者头像
iiopsd
发布于 2022-12-23 00:54:50
发布于 2022-12-23 00:54:50
9.6K41
代码可运行
举报
文章被收录于专栏:iiopsd技术专栏iiopsd技术专栏
运行总次数:1
代码可运行

背景

现在需要将一个导出列表数据到Excel表格的功能进行改造,将指定列相同数据自动合并单元格。

如上图所示,指定A、B两列自动合并,如图所示(6、7),(8、9),(13、14、15)要自动合并单元格。

EasyExcel 介绍

EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。

EasyExcel相比其他Excel解析框架(Apache poi和jxl),拥有更好的内存消耗管理算法。特别是对07版Excel的解决,EasyExcel重写了底层解析逻辑,一个3M的Excel解析只需要几M内存,但是用poi解析可能需要100M左右的内存。EasyExcel提高了读取性能,64M内存20秒读取75M的Excel,还有更快的极速模式,但是消耗的内存会更多一些。

EasyExcel支持自定义策略合并单元格,可以方便快捷填充数据到模板中,有活跃的中文社区支持,完善的测试用例可以覆盖大部分业务场景的使用。

合并单元格案例讲解

使用EasyExcel导出Excel代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
	@Test
    public void testWrite() throws IOException {
        List<DemoMergeData> resultList = new ArrayList<>();
        resultList.add(DemoMergeData.builder().id(1).sub("张胜男").date("12").build());
        resultList.add(DemoMergeData.builder().id(1).sub("李四").date("224").build());
        resultList.add(DemoMergeData.builder().id(3).sub("王五").date("224").build());
        resultList.add(DemoMergeData.builder().id(4).sub("赵柳").date("224").build());
        resultList.add(DemoMergeData.builder().id(5).sub("赵柳").date("224").build());
        resultList.add(DemoMergeData.builder().id(5).sub("赵柳").date("224").build());
        resultList.add(DemoMergeData.builder().id(8).sub("赵柳").date("224").build());
        resultList.add(DemoMergeData.builder().id(8).sub("赵柳").date("224").build());
        resultList.add(DemoMergeData.builder().id(9).sub("陈琪").date("224").build());
        resultList.add(DemoMergeData.builder().id(10).sub("小白").date("241").build());
        resultList.add(DemoMergeData.builder().id(11).sub("小黑").date("241").build());
        resultList.add(DemoMergeData.builder().id(12).sub("小黑").date("241").build());
        resultList.add(DemoMergeData.builder().id(12).sub("小黑").date("241").build());
        resultList.add(DemoMergeData.builder().id(12).sub("小黑").date("241").build());
        resultList.add(DemoMergeData.builder().id(13).sub("小黑").date("241").build());
        //  设置文件名称
        String fileName = "C:\\Users\\Administrator\\Downloads\\test\\t1.xlsx";
        File file = new File(fileName);
        if (!file.exists()) {
            file.createNewFile();
        }

        //  sheet名称
        EasyExcel.write(fileName, DemoMergeData.class)
                .autoCloseStream(Boolean.TRUE)
                .sheet("测试导出").doWrite(resultList);
    }

导出表格样式:

自定义策略一:上下行数据相同合并单元格

自定义策略一代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ExcelFillCellMergeStrategy implements CellWriteHandler {

    private int[] mergeColumnIndex;
    private int mergeRowIndex;
    
    public ExcelFillCellMergeStrategy() {
    }

    public ExcelFillCellMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }


    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
        //当前行
        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 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) {

    }
}

测试代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
    public void testWrite() throws IOException {
        int[] mergeColumnIndex = {0,1};
	    // 需要从第几行开始合并
        int mergeRowIndex = 1;
        // 数据就不初始化了
        List<DemoMergeData> resultList = new ArrayList<>();
        // 设置文件名称
        String fileName = "C:\\Users\\Administrator\\Downloads\\test\\t1.xlsx";
        File file = new File(fileName);
        if (!file.exists()) {
            file.createNewFile();
        }

        //  sheet名称
        EasyExcel.write(fileName, DemoMergeData.class)
                .autoCloseStream(Boolean.TRUE)
                .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex,mergeColumnIndex))
                .sheet("测试导出").doWrite(resultList);
    }

导出样式:

自定义策略二:指定列数据都相同才合并单元格

自定义策略二代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MultiColumnMergeStrategy extends AbstractMergeStrategy {

    // 合并的列编号,从0开始,指定的index或自己按字段顺序数
    private Integer startCellIndex = 0;
    private Integer endCellIndex = 0;

    // 数据集大小,用于区别结束行位置
    private Integer maxRow = 0;

    // 禁止无参声明
    private MultiColumnMergeStrategy() {
    }


    public MultiColumnMergeStrategy(Integer maxRow, Integer startCellIndex, Integer endCellIndex) {
        this.startCellIndex = startCellIndex;
        this.endCellIndex = endCellIndex;
        this.maxRow = maxRow;
    }

    // 记录上一次合并的信息
    private final List<List<String>> dataList = new ArrayList<>();

    /**
     * 每行每列都会进入,循环注意条件限制
     */
    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) {
        int currentCellIndex = cell.getColumnIndex();
        int currentRowIndex = cell.getRowIndex();

        // 判断该列是否需要合并
        if (currentCellIndex < startCellIndex || currentCellIndex > endCellIndex) {
            return;
        }

        Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        String currentCellValue = curData.toString();

        List<String> rowList;
        if (dataList.size() > currentRowIndex - 1) {
            rowList = dataList.get(currentRowIndex - 1);
        } else {
            rowList = new ArrayList<>();
            dataList.add(rowList);
        }
        rowList.add(currentCellValue);

        // 结束的位置触发下最后一次没完成的合并
        if (relativeRowIndex == (maxRow - 1) && currentCellIndex == endCellIndex) {
            System.out.println(JSONObject.toJSONString(dataList));
            List<String> tempList = null;
            Integer tempIndex = null;
            for (int i = 0; i < dataList.size(); i++) {
                if (tempList == null) {
                    tempList = dataList.get(i);
                    tempIndex = i;
                    continue;
                }
                List<String> currList = dataList.get(i);
                if (tempList.equals(currList)) {
                    if (i >= dataList.size() - 1) {
                        // 结束的位置触发下最后一次没完成的合并
                        for (int j = 0; j < tempList.size(); j++) {
                            sheet.addMergedRegionUnsafe(new CellRangeAddress(tempIndex + 1, i + 1, startCellIndex + j, startCellIndex + j));
                        }
                    }
                    continue;
                }

                // 当前行数据和上一行数据不同且上面有多行相同数据时触发合并
                if (i - tempIndex > 1) {
                    for (int j = 0; j < tempList.size(); j++) {
                        sheet.addMergedRegionUnsafe(new CellRangeAddress(tempIndex + 1, i, startCellIndex + j, startCellIndex + j));
                    }
                }


                tempIndex = i;
                tempList = currList;


            }

        }
    }
}

测试代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
    public void testWrite() throws IOException {
        // 数据就不初始化了
        List<DemoMergeData> resultList = new ArrayList<>();
        // 设置文件名称
        String fileName = "C:\\Users\\Administrator\\Downloads\\test\\t1.xlsx";
        File file = new File(fileName);
        if (!file.exists()) {
            file.createNewFile();
        }

        //  sheet名称
        EasyExcel.write(fileName, DemoMergeData.class)
                .autoCloseStream(Boolean.TRUE)
                .registerWriteHandler(new MultiColumnMergeStrategy(resultList.size(),0,1))
                .sheet("测试导出").doWrite(resultList);
    }

导出样式:

总结

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
4 条评论
热度
最新
您好,请问为什么我导出时,第一行的数据没有被合并,就是前三行的前多少列都是一样的数据,要合并,但是第一行数据未合并,下面两行合并了,这是为什么?
您好,请问为什么我导出时,第一行的数据没有被合并,就是前三行的前多少列都是一样的数据,要合并,但是第一行数据未合并,下面两行合并了,这是为什么?
111举报
哥们资格研究吧,文档缺失加上低质量文章遍地跑还不如自己多调试下试试。。。
哥们资格研究吧,文档缺失加上低质量文章遍地跑还不如自己多调试下试试。。。
回复回复点赞举报
为了吐槽特地注册账号,什么垃圾文章,不知哪里抄来的垃圾代码,这文章唯一有用的就是让我看明白了合并的api,写的都是错的,tm出来丢人现眼,csdn的低质量蔓延如此严重??????
为了吐槽特地注册账号,什么垃圾文章,不知哪里抄来的垃圾代码,这文章唯一有用的就是让我看明白了合并的api,写的都是错的,tm出来丢人现眼,csdn的低质量蔓延如此严重??????
回复回复点赞举报
好文,顶了
好文,顶了
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
EasyExcel导出自定义合并单元格策略
//需要合并的列 int[] mergeColumeIndex = {0,1,2,3,4,5,8,9,11}; // 从那一列开始合并 int mergeRowIndex = 0; ExcelWriter excelWriter = EasyExcel.write(outputStream) .sheet("SheetName") //设置合并单元格策略 .registerWriteHandler(new ExcelFillCellMergeStrategy
时光_赌徒
2020/07/30
17.1K1
Java 导出 Excel,相同列数据相同的情况下合并单元格【POI的相关依赖自行百度添加】
Java 导出 Excel,相同列数据相同的情况下合并单元格【POI的相关依赖自行百度添加】
无忧摸鱼
2022/05/31
4.4K0
Java 导出 Excel,相同列数据相同的情况下合并单元格【POI的相关依赖自行百度添加】
Spring Boot + EasyExcel导入导出,简直太好用了!
老项目主要采用的POI框架来进行Excel数据的导入和导出,但经常会出现OOM的情况,导致整个服务不可用。后续逐步转移到EasyExcel,简直不能太好用了。
程序新视界
2022/08/03
4.5K0
Spring Boot + EasyExcel导入导出,简直太好用了!
如何用 JAVA 实现一个基于 POI 的复杂表格导出工具类?
项目中有一些工程表格需要导出,设计到行列合并,定制样式,原有工具类冗余,内聚性强。所以想写一个可以随意定制excel的工具类,工具类满足需求:
山河已无恙
2023/01/30
1.5K0
如何用 JAVA 实现一个基于 POI 的复杂表格导出工具类?
SpringBoot系列之集成EasyExcel导入合并行数据
最近在做Excel导入功能,是一种一对多的数据,涉及到合并单元格的,考虑到使用poi去学,要自己去做处理,所以,看看有什么开源的框架,找到两个合适的框架,一个是easypoi是能支持这种的,这个框架提供了特定注解;还有一种是EasyExcel,阿里开源的,不过功能相对没easypoi齐全,比如这种合并单元格数据导入,就没有特定的注解,不过通过搜索资料,是可以实现的,不过要自己写工具类做处理,工具类整理自网上教程
SmileNicky
2022/05/07
1.9K0
SpringBoot系列之集成EasyExcel导入合并行数据
JAVA连接Excel最好用的开源项目EasyExcel,官方使用文档及.jar包下载
我使用后,觉得阿里确实很用心,使用简单,速度还快,这个⭐可以给。 如果以下内容还是看不懂,给他们点个小星星,然后我教你,嘻嘻。
风骨散人Chiam
2020/10/28
9.6K0
【案例实战】SpringBoot整合EasyExcel实现列表导出功能
这篇文章会给大家实操一个关于列表导出成excel表格的功能,相信大家在日常工作中也会遇到列表导出的需求,看完本篇文章那么你就可以轻松的去整合列表导出的功能。
互联网小阿祥
2023/05/28
8140
【案例实战】SpringBoot整合EasyExcel实现列表导出功能
springboot项目导出excel 合并单元格表格
六月的雨在Tencent
2024/03/28
2010
springboot项目导出excel 合并单元格表格
SpringBoot处理Exce表格Demo
代码:controller层里应该放service层,可以把excel的逻辑代码根据实际情况处理一下,本文没有处理
CBeann
2023/12/25
1670
java相关工具类(excel导出)
工具类 package com.longrise.SWMS.Util; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.L
java攻城狮
2020/10/10
2.5K0
史上最全excel导入导出技能盘点
#简介 poi使用userModel模式,这个模式的特点就是上手很容易。代码写起来很复杂。而且公用的地方很少。导致每次读写excel都需要重新编写。 EasyExcel使用SAX模式使得easyexcel可以节省内存。而且easyexcel解决了内存泄漏问题。如果想了解SAX模式开发那成本需要3~5天学习。
啵啵肠
2023/11/29
3330
EasyExcel合并单元行没填不能解析数据
最近在做Excel报表开发,要开发一个一对多,合并单元行的Excel导入功能,因为开发时间比较赶,所以想到使用开源的EasyExcel组件来开发,不过在开发中遇到一个问题,就是那些合并单元格,一整行都不填的情况,使用EasyExcel去解析数据时候,是一张行数据都不能解析到,使用过EasyExcel的读者应该可以理解到我的意思,写出来分享出来,希望遇到这个问题的开发者也可以马上处理问题
SmileNicky
2022/05/07
5590
EasyExcel合并单元行没填不能解析数据
EasyExcel实现文件上传下载(百万级数据、单元格自定义样式)
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。 EasyExcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。
冬天vs不冷
2025/01/21
1730
EasyExcel实现文件上传下载(百万级数据、单元格自定义样式)
Java实现Excel导入和导出,看这一篇就够了(珍藏版)
最近抽了两天时间,把Java实现表格的相关操作进行了封装,本次封装是基于 POI 的二次开发,最终使用只需要调用一个工具类中的方法,就能满足业务中绝大部门的导入和导出需求。
全栈程序员站长
2022/09/14
3.9K0
Java实现Excel导入和导出,看这一篇就够了(珍藏版)
Apache POI与easyExcel:Excel文件导入导出的技术深度分析
下面是一演示如何使用 Apache POI 导入(读取)和导出(写入)Excel 文件(.xlsx 格式)
公众号:码到三十五
2024/03/19
1.7K0
Apache POI与easyExcel:Excel文件导入导出的技术深度分析
Excel解析工具easyexcel全面探索
之前我们想到Excel解析一般是使用POI,但POI存在一个严重的问题,就是非常消耗内存。所以阿里人员对它进行了重写从而诞生了easyexcel,它解决了过于消耗内存问题,也对它进行了封装让使用者使用更加便利
老梁
2019/10/25
4.6K0
Excel解析工具easyexcel全面探索
SpringBoot 实现 Excel 导入导出,性能爆表,用起来够优雅!
EasyExcel是一款阿里开源的Excel导入导出工具,具有处理快速、占用内存小、使用方便的特点,在Github上已有22k+Star,可见其非常流行。
macrozheng
2022/01/17
2.8K0
SpringBoot 实现 Excel 导入导出,性能爆表,用起来够优雅!
POI判断某个单元格是否是合并单元格
注:比如我们的数据是上面N个单元组成,且每个单元所占行数可能不同。第一列占据一列,中间数据每个占用一个单元格,最后一列与第一列占用相同的行数,这时我们需要获取起始单元格占用几行(起始行--结束行),获取到这些数据后我们就能读取中间单元格数据(这些数据可以作为上面单元的一个属性),下面给出具体代码:
johnhuster的分享
2022/03/28
3.2K0
POI判断某个单元格是否是合并单元格
java按需导出Excel并自动合同单元格
最近公司有一个需求,就是按到模版导出数据报表,并内容相同的单元格实现自动合并.具体业务设计图如下所示
java攻城狮
2020/11/30
1.1K0
java按需导出Excel并自动合同单元格
Python操作Excel合并单元格
每门编程语言都会遇到操作Excel!本文主要说下Python对Excel操作时合并单元格的情况。
酒馆丁老师
2020/09/08
8.1K0
Python操作Excel合并单元格
推荐阅读
相关推荐
EasyExcel导出自定义合并单元格策略
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档