当前位置: 代码网 > it编程>编程语言>Java > springboot之excel工具类使用及说明

springboot之excel工具类使用及说明

2026年04月07日 Java 我要评论
背景后台需要批量处理excel数据,常见的就是导入导出了。故自己写了一个小工具。依赖<dependency> <groupid>com.alibaba</group

背景

后台需要批量处理excel数据,常见的就是导入导出了。故自己写了一个小工具。

依赖

<dependency>
    <groupid>com.alibaba</groupid>
    <artifactid>easyexcel</artifactid>
    <version>3.3.4</version>
</dependency>

工具类

package org.test.exceldemo.excel;

import com.alibaba.excel.easyexcel;
import com.alibaba.excel.excelwriter;
import com.alibaba.excel.enums.celldatatypeenum;
import com.alibaba.excel.metadata.head;
import com.alibaba.excel.metadata.data.celldata;
import com.alibaba.excel.metadata.data.writecelldata;
import com.alibaba.excel.read.listener.pagereadlistener;
import com.alibaba.excel.write.metadata.writesheet;
import com.alibaba.excel.write.metadata.holder.writesheetholder;
import com.alibaba.excel.write.style.column.abstractcolumnwidthstylestrategy;
import com.alibaba.excel.write.style.column.longestmatchcolumnwidthstylestrategy;
import org.apache.poi.ss.usermodel.cell;
import org.apache.poi.ss.usermodel.sheet;
import org.springframework.util.collectionutils;
import org.springframework.web.multipart.multipartfile;

import java.io.*;
import java.net.url;
import java.util.hashmap;
import java.util.list;
import java.util.map;
import java.util.function.consumer;
import java.util.function.function;

/**
 * excel 导入导出工具类
 * 包含只读写、只写、流式读、流式写
 */
public class easyexcelpipelineutil {
    /**
     * ⭐⭐⭐ 同步读取(小文件用)
     */
    public static <t> list<t> readsync(inputstream inputstream, class<t> clazz) {
        return easyexcel.read(inputstream)
                .head(clazz)
                .sheet()
                .doreadsync();
    }

    /**
     * ⭐⭐⭐ 分页读取(推荐)
     */
    public static <t> void readbypage(inputstream inputstream,
                                      class<t> clazz,
                                      consumer<list<t>> consumer,
                                      int pagesize) {

        easyexcel.read(inputstream, clazz,
                        new pagereadlistener<t>(consumer, pagesize))
                .sheet()
                .doread();
    }

    /**
     * ⭐⭐⭐ multipartfile 分页读取
     */
    public static <t> void readbymultipart(multipartfile file,
                                           class<t> clazz,
                                           consumer<list<t>> consumer,
                                           int pagesize) throws ioexception {

        readbypage(file.getinputstream(), clazz, consumer, pagesize);
    }

    /**
     * ⭐⭐⭐ url 分页读取
     */
    public static <t> void readbyurl(string url,
                                     class<t> clazz,
                                     consumer<list<t>> consumer,
                                     int pagesize) throws exception {

        try (inputstream in = new url(url).openstream()) {
            readbypage(in, clazz, consumer, pagesize);
        }
    }

    /**
     * ====================================
     * ⭐⭐⭐⭐⭐ 直接写 excel(最常用导出)
     * ====================================
     */
    public static <t> void write(string filepath,
                                 class<t> clazz,
                                 list<t> data, string sheetname) {

        easyexcel.write(filepath, clazz)
                .registerwritehandler(new longestmatchcolumnwidthstylestrategy())
                .registerwritehandler(new excelcellwritewidthconfig())
                .sheet(sheetname)
                .dowrite(data);
    }

    /**
     * ⭐ 写到 outputstream(浏览器下载用)
     */
    public static <t> void write(outputstream outputstream,
                                 class<t> clazz,
                                 list<t> data, string sheetname) {

        easyexcel.write(outputstream, clazz)
                .registerwritehandler(new longestmatchcolumnwidthstylestrategy())
                .registerwritehandler(new excelcellwritewidthconfig())
                .sheet(sheetname)
                .dowrite(data);
    }

    /**
     * ⭐⭐⭐⭐ 超大数据流式写(百万行推荐)
     */
    public static <t> void writestream(string filepath,
                                       class<t> clazz,
                                       consumer<excelwriter> writerconsumer) {

        excelwriter writer = easyexcel.write(filepath, clazz)
                .registerwritehandler(new longestmatchcolumnwidthstylestrategy())
                .registerwritehandler(new excelcellwritewidthconfig())
                .build();

        try {
            writerconsumer.accept(writer);
        } finally {
            writer.finish();
        }
    }
// 调用样例
//    easyexcelutil.writestream(
//            "d:/big.xlsx",
//    userexportvo.class,
//    writer -> {
//
//        for (int i = 0; i < 100; i++) {
//
//            list<userexportvo> page =
//                    userservice.querypage(i);
//
//            easyexcelutil.writebatchsheet(
//                    writer,
//                    page,
//                    0,
//                    "用户表"
//            );
//        }
//    }
//);

    /**
     * ⭐ 分批写 sheet
     */
    public static <t> void writebatchsheet(excelwriter writer,
                                           list<t> data,
                                           int sheetno,
                                           string sheetname) {

        writesheet sheet = easyexcel.writersheet(sheetno, sheetname)
                .registerwritehandler(new longestmatchcolumnwidthstylestrategy())
                .registerwritehandler(new excelcellwritewidthconfig())
                .build();
        writer.write(data, sheet);
    }

    /**
     * ⭐⭐⭐ 核心流式分页处理(真正生产级)
     * ⭐⭐⭐ 不关心读写,只需要处理数据即可,包含excel文件的读写
     */
    private static <in, out> void processstream(
            inputstream inputstream,
            outputstream outputstream,
            class<in> inclass,
            class<out> outclass,
            function<list<in>, list<out>> mapper,
            int pagesize) {

        excelwriter writer = easyexcel.write(outputstream, outclass)
                        .registerwritehandler(new longestmatchcolumnwidthstylestrategy())
                        .registerwritehandler(new excelcellwritewidthconfig())
                        .build();
        writesheet sheet = easyexcel.writersheet("result").build();

        easyexcel.read(inputstream, inclass,
                        new pagereadlistener<in>(pagedata -> {
                            list<out> result = mapper.apply(pagedata);
                            writer.write(result, sheet);
                        }, pagesize))
                .sheet()
                .doread();

        writer.finish();
    }

    /**
     * =========================
     * ⭐ 本地文件路径
     * =========================
     */
    public static <in, out> void processbyfilepath(
            string inputfile,
            string outputfile,
            class<in> inclass,
            class<out> outclass,
            function<list<in>, list<out>> mapper,
            int pagesize) throws exception {

        try (inputstream in = new fileinputstream(inputfile);
             outputstream out = new fileoutputstream(outputfile)) {

            processstream(in, out, inclass, outclass, mapper, pagesize);
        }
    }

    /**
     * =========================
     * ⭐ url 网络文件
     * =========================
     */
    public static <in, out> void processbyurl(
            string fileurl,
            string outputfile,
            class<in> inclass,
            class<out> outclass,
            function<list<in>, list<out>> mapper,
            int pagesize) throws exception {

        try (inputstream in = new url(fileurl).openstream();
             outputstream out = new fileoutputstream(outputfile)) {

            processstream(in, out, inclass, outclass, mapper, pagesize);
        }
    }

    /**
     * =========================
     * ⭐ multipartfile(web上传)
     * =========================
     */
    public static <in, out> void processbymultipart(
            multipartfile file,
            string outputfile,
            class<in> inclass,
            class<out> outclass,
            function<list<in>, list<out>> mapper,
            int pagesize) throws exception {
        try (inputstream in = file.getinputstream();
             outputstream out = new fileoutputstream(outputfile)) {

            processstream(in, out, inclass, outclass, mapper, pagesize);
        }
    }

    /**
     * =========================
     * ⭐ inputstream 通用入口
     * =========================
     */
    public static <in, out> void processbystream(
            inputstream inputstream,
            outputstream outputstream,
            class<in> inclass,
            class<out> outclass,
            function<list<in>, list<out>> mapper,
            int pagesize) {

        processstream(inputstream, outputstream, inclass, outclass, mapper, pagesize);
    }

    /**
     * 自动计算列宽
     *
     */
    private static class excelcellwritewidthconfig extends abstractcolumnwidthstylestrategy {
        private final map<integer, map<integer, integer>> cache = new hashmap<>();

        @override
        protected void setcolumnwidth(writesheetholder writesheetholder, list<writecelldata<?>> celldatalist, cell cell, head head, integer integer, boolean ishead) {
            boolean needsetwidth = ishead || !collectionutils.isempty(celldatalist);
            if (needsetwidth) {
                map<integer, integer> maxcolumnwidthmap = cache.computeifabsent(writesheetholder.getsheetno(), k -> new hashmap<>());

                integer columnwidth = this.datalength(celldatalist, cell, ishead);
                // 单元格文本长度大于60换行
                if (columnwidth >= 0) {
                    if (columnwidth > 60) {
                        columnwidth = 60;
                    }
                    integer maxcolumnwidth = maxcolumnwidthmap.get(cell.getcolumnindex());
                    if (maxcolumnwidth == null || columnwidth > maxcolumnwidth) {
                        maxcolumnwidthmap.put(cell.getcolumnindex(), columnwidth);
                        sheet sheet = writesheetholder.getsheet();
                        sheet.setcolumnwidth(cell.getcolumnindex(), columnwidth * 2 * 256);
                    }
                }
            }
        }

        /**
         * 计算长度
         */
        private integer datalength(list<writecelldata<?>> celldatalist, cell cell, boolean ishead) {
            if (ishead) {
                return cell.getstringcellvalue().getbytes().length;
            } else {
                celldata<?> celldata = celldatalist.get(0);
                celldatatypeenum type = celldata.gettype();
                if (type == null) {
                    return -1;
                } else {
                    switch (type) {
                        case string:
                            // 换行符(数据需要提前解析好)
                            int index = celldata.getstringvalue().indexof("\n");
                            return index != -1 ?
                                    celldata.getstringvalue().substring(0, index).getbytes().length + 1 : celldata.getstringvalue().getbytes().length + 1;
                        case boolean:
                            return celldata.getbooleanvalue().tostring().getbytes().length;
                        case number:
                            return celldata.getnumbervalue().tostring().getbytes().length;
                        default:
                            return -1;
                    }
                }
            }
        }
    }
}

举例

定义exel文件数据类,定义数据处理函数即可。

导入类

package org.test.exceldemo.excel;

import com.alibaba.excel.annotation.excelproperty;
import lombok.data;

/**
 *  导入数据类,对应excel表格字段
 */
@data
public class userimportvo {

    @excelproperty("user name")
    private string name;

    @excelproperty("user age")
    private integer age;
}

导出类

package org.test.exceldemo.excel;

import com.alibaba.excel.annotation.excelproperty;
import lombok.data;

/**
 * 导入结果类
 */
@data
public class userimportresultvo {

    @excelproperty("user name")
    private string name;

    @excelproperty("user age")
    private integer age;

    @excelproperty("import result")
    private string result;
}

数据处理函数类

package org.test.exceldemo.excel;

import org.springframework.beans.beanutils;
import org.springframework.stereotype.service;

import java.util.arraylist;
import java.util.list;
import java.util.set;

/**
 *  
 */
@service
public class testexceldemoservice {
    /**
     * excel数据处理类
     * @param list excel数据
     * @param globalunique 全局唯一
     * @return 处理结果
     */
    public list<userimportresultvo> convertpage(list<userimportvo> list, set<string> globalunique) {

        list<userimportresultvo> result = new arraylist<>();

        for (userimportvo in : list) {
            userimportresultvo out = new userimportresultvo();
            beanutils.copyproperties(in, out);
            // 判断数据重复
            if (!globalunique.add(in.getname())){
                out.setresult("重复数据");
                result.add(out);
                continue;
            }
            // 数据处理逻辑

            // 处理结果
            out.setresult("success");
            result.add(out);
        }
        return result;
    }
}

模拟请求处理类

@restcontroller
@requestmapping("/")
public class testexcelcontroller {
    @resource
    private testexceldemoservice testexceldemoservice;

    @postmapping(path = "upload", consumes = mediatype.multipart_form_data_value)
    public string uploadfile(@requestparam("file") multipartfile file) throws exception {
        //通过文件后缀名或文件魔数进行更可靠的校验
        string originalfilename = file.getoriginalfilename();
        // 只处理excel文件
        if (originalfilename != null && !originalfilename.tolowercase().endswith(".xlsx")) {
            return "仅支持 excel 文件";
        }

        set<string> globalunique = new hashset<>();
        easyexcelpipelineutil.processbymultipart(file,
                "d:/output.xlsx",
                userimportvo.class,
                userimportresultvo.class,  list -> testexceldemoservice.convertpage(list, globalunique), 1000);
        return "上传成功";
    }
}

测试数据

模拟请求

请求文件数据

生成导出结果文件数据

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com