Java使用easyExcel轻松实现Excel文件合并

 更新时间:2026年04月03日 09:07:07   作者:诸葛大钢铁  
这篇文章主要为大家详细介绍了Java如何使用easyExcel轻松实现Excel文件合并,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

使用easyExcel工具实现Excel文件的合并,包含多文件合并、多sheet合并以及按照Sheet文件名字合并。

Excel文件合并,提供三种类型需求实现Excel合并。

1、合并多个excel,文件中没有包含sheet

2、Excel的文件sheet顺序合并sheet

3、合并多个sheet,按照两个文件的sheet名字合并

package org.example.engine;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.read.listener.*;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.*;
import com.alibaba.excel.write.metadata.WriteSheet;
import java.util.*;
public class AppendMergeFlow {
    //合并多个excel,文件中没有包含sheet
    public static void merge(List<String> inputFiles, String outputFile,Boolean deduplicate) {
        // 👉 创建 listener(核心)
        AppendMergeListener listener = new AppendMergeListener(outputFile,deduplicate);
        for (String file : inputFiles) {
            EasyExcel.read(file, listener)
                    .sheet()
                    .doRead();
        }
        // 👉 最后必须手动关闭 writer
        listener.finish();
    }
    //根据excel的文件sheet顺序合并sheet
    public static void mergeAllSheet(List<String> inputFiles, String outputFile,Boolean deduplicate){
//        ExcelWriter writer = EasyExcel.write(outputFile).build();
//        WriteSheet writeSheet = EasyExcel.writerSheet(0, "merged").build();
//        AppendMergeListener listener = new AppendMergeListener(writer, writeSheet, deduplicate);
        AppendMergeListener listener = new AppendMergeListener(outputFile,deduplicate);
        for (String file : inputFiles) {
            ExcelReader reader = EasyExcel.read(file).build();
            List<ReadSheet> sheets = reader.excelExecutor().sheetList();
            for (ReadSheet sheet : sheets) {
                // ⭐ 顺序读取每个sheet
                EasyExcel.read(file, listener)
                        .sheet(sheet.getSheetName())
                        .doRead();
            }
            reader.finish();
        }
        listener.finish();
    }
    //合并多个sheet,按照两个文件的sheet名字合并
    public static void mergeSheet(List<String> inputFiles, String outputFile,Boolean deduplicate){
        // 1️⃣ 收集所有 sheet
        Map<String, List<String>> sheetMap = collectSheets(inputFiles);
        System.out.println("sheetMap"+ " " +sheetMap.values());
        // 2️⃣ 创建 writer(多sheet输出)
        ExcelWriter writer = EasyExcel.write(outputFile).build();
//        AppendMergeListener listener = new AppendMergeListener(writer, writeSheet,deduplicate);
        int sheetIndex = 0;
        // 3️⃣ 每个sheet分别合并
        for (String sheetName : sheetMap.keySet()) {
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetIndex++, sheetName).build();
            System.out.println("sheeIndex:"+sheetIndex+";"+ " " +"sheetName:"+sheetName);
            AppendMergeListener listener = new AppendMergeListener(writer, writeSheet,deduplicate);
            for (String file : sheetMap.get(sheetName)) {
                System.out.println("sheetName:"+ " " +sheetName);
                EasyExcel.read(file, listener)
                        .sheet(sheetName) // ⭐ 指定sheet
                        .doRead();
            }
//            listener.finish(); // 可选
        }
        writer.finish();
    }
    private static Map<String, List<String>> collectSheets(List<String> files) {
        Map<String, List<String>> map = new LinkedHashMap<>();
        for (String file : files) {
            ExcelReader reader = EasyExcel.read(file).build();
            List<ReadSheet> sheets = reader.excelExecutor().sheetList();
            for (ReadSheet sheet : sheets) {
                String name = sheet.getSheetName();
                map.computeIfAbsent(name, k -> new ArrayList<>())
                        .add(file);
            }
            reader.finish();
        }
        return map;
    }
}

监听器AppendMergeListener,基于 AnalysisEventListener,使用 Map<Integer,String> 流式处理对Excel每一行的的内容处理。

package org.example.engine;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;

import java.util.*;


/**
 * AppendMergeListener:基于 AnalysisEventListener,使用 Map<Integer,String> 流式处理
 */

public class AppendMergeListener extends AnalysisEventListener<Map<Integer, String>> {

    private ExcelWriter writer;
    private WriteSheet sheet;
    private boolean deduplicate;


    private Set<String> seen = new HashSet<>();

    //全局表头
    private List<String> globalheader = null;

    // ⭐ 当前文件表头
    private List<String> currentHeader = null;

    // ⭐ 是否已经写过“统一表头”
    private boolean headerWritten = false;

    private int fileIndex = 1;
    private int rowCount = 0;

    // ⭐ Excel最大行(建议留余量)
    private static final int MAX_ROW = 1_000_000;

    private  String outputFile;


    //合并多个文件,并且去重
    public AppendMergeListener(String outputFile,Boolean deduplicate) {
//        this.writer = EasyExcel.write(outputFile).build();
        this.outputFile= outputFile;                  //添加
        initWriter();                                  //添加
        this.sheet = EasyExcel.writerSheet("Merged").build();
        this.deduplicate = deduplicate;
    }

    private void initWriter() {
        String fileName = outputFile + "_" + fileIndex + ".xlsx";
        writer = EasyExcel.write(fileName).build();
        rowCount = 0;
    }

    //合并多个文件
    public AppendMergeListener(String outputFile) {
        this.writer = EasyExcel.write(outputFile).build();
        this.outputFile= outputFile;                  //添加
        initWriter();                                  //添加
        this.sheet = EasyExcel.writerSheet("Merged").build();
    }

    //合并多个文件中的sheet
    public AppendMergeListener(ExcelWriter writer, WriteSheet sheet, Boolean deduplicate) {
        this.writer = writer;
        this.sheet = sheet;
        this.deduplicate = deduplicate;
    }




    // ✅ 正确处理表头
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {

        List<String> currentHeader = mapToList(headMap);

        if (globalheader == null){

            globalheader = new ArrayList<>(currentHeader);

            writer.write(Collections.singletonList(globalheader), sheet);

            headerWritten = true;
            rowCount++;

            return;
        }

        if (globalheader.equals(currentHeader)) {
            System.out.println("1:两者相同");
            System.out.println(currentHeader);

            System.out.println(globalheader);

        }else{
            System.out.println("2:两者不相同");
            System.out.println(currentHeader);

            System.out.println(globalheader);

            writer.write(Collections.singletonList(currentHeader), sheet);
            rowCount++;
        }
    }
    @Override
    public void invoke(Map<Integer, String> row, AnalysisContext context) {

        List<String> list = mapToList(row);

        if(rowCount >= MAX_ROW){
            writer.finish();
            fileIndex++;
            initWriter();
        }

        if (deduplicate) {
            String key = String.join("|", list);
            if (seen.contains(key)) return;
            seen.add(key);
        }

        writer.write(Collections.singletonList(list), sheet);

        rowCount++;

    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 不关闭!多个文件共用
    }


    public void finish() {
        if(writer!=null){      //添加的
            writer.finish();
        }

    }

    public List<String> getGlobalheader() {
        return globalheader;
    }

    private List<String> mapToList(Map<Integer, String> row) {
        List<String> list = new ArrayList<>();
        int maxIndex = row.keySet().stream().max(Integer::compareTo).orElse(0);

        for (int i = 0; i <= maxIndex; i++) {
            list.add(row.getOrDefault(i, ""));
        }

        return list;
    }

    public String getOutputFile() {
        return outputFile;
    }
}


测试文件:文件路径替换为你自己的文件,

package org.example;

import org.example.engine.AppendMergeFlow;
import org.example.engine.JoinMergeFlow;

import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class TestMain {

    public static void main(String[] args) throws FileNotFoundException {

        String file1="C:\\Users\\XXX\\Downloads\\excelmerge\\excel1.xlsx";
        String file2="C:\\Users\\XXX\\Downloads\\excelmerge\\excel2.xlsx";

        String file3="C:\\Users\\XXX\\Downloads\\excelmerge\\hm1.xlsx";
        String file4="C:\\Users\\XXX\\Downloads\\excelmerge\\hm2.xlsx";

        String file5="C:\\Users\\XXX\\Downloads\\excelmerge\\检测数据13-21-1222.xlsx";
        String file6="C:\\Users\\XXX\\Downloads\\excelmerge\\检测数据22-29-1222.xlsx";



//        // ✅ 1️⃣ Append(去重)
        //合并多Excel文件
        AppendMergeFlow.merge( Arrays.asList(file5, file6),
                "C:\\Users\\XXX\\Downloads\\excelmerge\\merged_append19.xlsx",true);
        //按照文件的sheet顺序合并,支持多文件合并
        AppendMergeFlow.mergeAllSheet( Arrays.asList(file5,file6),
                "C:\\Users\\XXX\\Downloads\\excelmerge\\merged_append_all1.xlsx",true);
        //按照文件名合并Sheet,支持多文件合并
        AppendMergeFlow.mergeSheet( Arrays.asList(file5,file6),
                "C:\\Users\\XXX\\Downloads\\excelmerge\\merged_append_all1.xlsx",true);


//         ✅ 2️⃣ Join(按第0列)
//        JoinMergeFlow.join(
//                file1,
//                file2,
//                "C:\\Users\\XXX\\Downloads\\excelmerge\\merged_join1.xlsx",
//                0
//        );

        System.out.println("全部完成!");
    }
}

导入对应的依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>mergeExcel</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
        <relativePath/>
    </parent>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- EasyExcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.3.2</version>
        </dependency>
        <!-- Lombok(可选) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

合并效果:

到此这篇关于Java使用easyExcel轻松实现Excel文件合并的文章就介绍到这了,更多相关Java easyExcel合并Excel内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java判断字符串中是否包含中文方法

    Java判断字符串中是否包含中文方法

    这篇文章主要介绍了Java判断字符串中是否包含中文方法,使用Matcher类解决了这个问题,需要的朋友可以参考下
    2014-06-06
  • Mac电脑如何通过 IntelliJ IDEA 远程连接 MySQL

    Mac电脑如何通过 IntelliJ IDEA 远程连接 MySQL

    本文详解Mac通过IntelliJ IDEA远程连接MySQL的步骤,本文通过图文并茂的形式给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-08-08
  • 解决spring-data-jpa mysql建表编码问题

    解决spring-data-jpa mysql建表编码问题

    这篇文章主要介绍了解决spring-data-jpa mysql建表编码问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java中RedisUtils工具类的使用

    Java中RedisUtils工具类的使用

    本文主要介绍了Java中RedisUtils工具类的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-07-07
  • Java Deque基本概念和使用方法

    Java Deque基本概念和使用方法

    Deque双端队列是Java Collections Framework的一部分,支持在两端插入和删除操作,它继承自Queue接口,可以作为队列FIFO或栈LIFO使用,本文介绍java Deque基本概念和使用方法,感兴趣的朋友一起看看吧
    2025-03-03
  • 详解Spring如何使用@Scheduled注解执行定时任务

    详解Spring如何使用@Scheduled注解执行定时任务

    在现代的Java应用程序中,定时任务是一种常见的需求,Spring框架提供了多种方式来管理定时任务,其中​​@Scheduled​​注解因其简洁和易用性而受到开发者的青睐,下面我们就来看看具体实现方法吧
    2025-08-08
  • Java RabbitMQ高级特性详细分析

    Java RabbitMQ高级特性详细分析

    为了保证消息的可靠性传输,包括投递消息的生产方能投递成功,和消息消费的消费方正确消费,RabbitMQ 提供了两个确认机制,由于消息按照流通的顺序从左到右,因此为保证可靠性,MQ必须对 Producer进行确认,Consumer 必须对 MQ 进行确认
    2022-08-08
  • Java实现的图片上传工具类完整实例

    Java实现的图片上传工具类完整实例

    这篇文章主要介绍了Java实现的图片上传工具类,涉及java针对图片文件的检查、上传、清除等相关操作技巧,需要的朋友可以参考下
    2017-10-10
  • Springboot整合Jedis实现单机版或哨兵版可切换配置方法

    Springboot整合Jedis实现单机版或哨兵版可切换配置方法

    这篇文章主要介绍了Springboot整合Jedis实现单机版或哨兵版可切换配置方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-11-11
  • GateWay中StripPrefix的作用及说明

    GateWay中StripPrefix的作用及说明

    StripPrefix过滤器剥离路径前缀,PrefixPath则添加前缀,例如,访问http://host:port/lbs/hello时,StripPrefix=1会将请求转至http://bds-lbs-service/hello,PrefixPath会添加/lbs前缀,两者作用相反,用于路由路径调整
    2025-07-07

最新评论