Java实现Excel文件合并

java代码实现Excel文件合并

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

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

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

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

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

java 复制代码
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每一行的的内容处理。

java 复制代码
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;
    }
}

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

java 复制代码
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 复制代码
<?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>

合并效果:

相关推荐
黎明丶之前2 小时前
Spring Cloud Gateway 升级与 Bucket4j 限流实践
java·spring cloud
程序员木圭2 小时前
05-告别逻辑混乱!Java 流程控制让代码学会"判断和循环"
java·后端
yaaakaaang2 小时前
三、抽象工厂模式
java·抽象工厂模式
kongba0072 小时前
复刻 Claude Code 项目御马术缰绳系统 harness engineering 落地蓝图
java·linux·服务器
tERS ERTS2 小时前
Spring Cloud gateway 路由规则
java
ZUNr12 小时前
手写一个迷你版 @Column:注解到底是怎么工作的?
java
习惯就好zz2 小时前
记一次 Mac SSH 免密登录 Windows 的踩坑与修复
windows·macos·ssh
csdn2015_2 小时前
Set<String> 类型取第一条记录
开发语言·windows·python
csdn2015_2 小时前
List<String> 转换为Set<String>
windows·python·list