Spring Boot集成EasyExcel

news2025/3/14 11:07:31

1. 初始化Spring Boot项目

首先,使用Spring Initializr(https://start.spring.io/)生成一个基本的Spring Boot项目。选择以下依赖项:

  • Spring Web
  • Lombok (用于减少样板代码)
  • SLF4J (用于日志记录)

2. 添加依赖

在你的pom.xml文件中添加EasyExcel的Maven依赖。确保版本号是最新的,你可以访问Maven仓库来获取最新版。

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>最新的版本号</version>
</dependency>

3. 创建实体类

假设我们需要处理一个用户信息表,包含姓名和年龄两个字段。以下是实体类的设计,并使用Lombok简化代码:

package com.example.demo.model;

import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

// 使用@Data注解自动生成getter、setter等方法
@Data
public class UserData {
    // 指定Excel列标题为“姓名”
    @ExcelProperty("姓名")
    private String name; // 用户姓名

    // 指定Excel列标题为“年龄”
    @ExcelProperty("年龄")
    private Integer age; // 用户年龄
}

4. 创建监听器类

创建一个监听器类来处理每一行的数据,并在服务类中调用它。我们使用@Slf4j注解简化日志记录:

package com.example.demo.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.demo.model.UserData;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

@Slf4j // 使用@Slf4j注解简化日志记录
public class UserDataListener extends AnalysisEventListener<UserData> {

    private final List<UserData> dataList = new ArrayList<>(); // 存储读取的数据

    /**
     * 当解析一行数据时调用
     * @param userData 解析得到的用户数据
     * @param analysisContext 上下文对象
     */
    @Override
    public void invoke(UserData userData, AnalysisContext analysisContext) {
        dataList.add(userData); // 将每一行的数据添加到列表中
        log.info("读取到一条数据: {} {}", userData.getName(), userData.getAge()); // 日志记录
    }

    /**
     * 所有数据解析完成后调用
     * @param analysisContext 上下文对象
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        log.info("所有数据解析完成"); // 数据解析完成后记录日志
    }

    /**
     * 获取读取的数据列表
     * @return 读取的数据列表
     */
    public List<UserData> getDataList() {
        return dataList; // 返回读取的数据列表
    }
}

5. 实现服务类

编写一个服务类来实现数据的读写操作,并增加异常处理和日志记录。我们将在此处添加分页功能,以确保当单个页面数据达到一定量时重新生成新的页面进行写入:

package com.example.demo.service;

import com.alibaba.excel.EasyExcel;
import com.example.demo.listener.UserDataListener;
import com.example.demo.model.UserData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Slf4j // 使用@Slf4j注解简化日志记录
@Service
public class ExcelService {

    private static final int PAGE_SIZE = 100; // 每页最大数据条数

    /**
     * 写入Excel文件
     * @param filePath 文件路径
     * @param data 要写入的数据列表
     */
    public void writeExcel(String filePath, List<UserData> data) {
        try {
            log.info("开始写入Excel文件: {}", filePath); // 记录日志

            int totalRows = data.size();
            int totalPages = (int) Math.ceil((double) totalRows / PAGE_SIZE);

            for (int page = 0; page < totalPages; page++) {
                int fromIndex = page * PAGE_SIZE;
                int toIndex = Math.min(fromIndex + PAGE_SIZE, totalRows);

                List<UserData> currentPageData = data.subList(fromIndex, toIndex);

                String sheetName = "用户信息" + (page + 1); // 设置工作表名称

                // 开始写入Excel文件
                EasyExcel.write(filePath, UserData.class)
                        .sheet(sheetName) // 设置工作表名称
                        .doWrite(currentPageData); // 执行写入操作

                log.info("写入第 {} 页数据完成", page + 1); // 写入完成后记录日志
            }

            log.info("写入Excel文件完成"); // 写入完成后记录日志
        } catch (Exception e) {
            log.error("写入Excel失败", e); // 记录错误日志
        }
    }

    /**
     * 读取所有工作表的Excel文件
     * @param inputStream 输入流
     * @return 读取的数据列表
     */
    public List<UserData> readAllSheets(InputStream inputStream) {
        List<UserData> allData = new ArrayList<>();
        try {
            log.info("开始读取所有工作表的Excel文件"); // 记录日志

            // 获取Excel文件中的所有Sheet信息
            List<String> sheetNames = EasyExcel.read(inputStream).excelExecutor().sheetList().get();

            for (String sheetName : sheetNames) {
                log.info("开始读取工作表: {}", sheetName);

                UserDataListener listener = new UserDataListener();
                // 执行读取操作
                EasyExcel.read(inputStream, UserData.class, listener)
                        .sheet(sheetName) // 指定工作表名称
                        .doRead(); // 执行读取操作

                // 处理listener.getDataList()
                allData.addAll(listener.getDataList());
                log.info("读取工作表 {} 完成", sheetName);
            }

            log.info("读取所有工作表的Excel文件完成"); // 读取完成后记录日志
        } catch (Exception e) {
            log.error("读取所有工作表的Excel失败", e); // 记录错误日志
        }
        return allData;
    }

    /**
     * 读取特定工作表的Excel文件
     * @param filePath 文件路径
     * @param sheetIndex 工作表索引(从0开始)
     */
    public void readSpecificSheet(String filePath, int sheetIndex) {
        try {
            log.info("开始读取特定工作表的Excel文件: {}, Sheet Index: {}", filePath, sheetIndex); // 记录日志

            UserDataListener listener = new UserDataListener();
            // 执行读取操作
            EasyExcel.read(filePath, UserData.class, listener)
                    .sheet(sheetIndex) // 指定工作表索引
                    .doRead(); // 执行读取操作

            // 处理listener.getDataList()
            for (UserData userData : listener.getDataList()) {
                log.info("{} {}", userData.getName(), userData.getAge()); // 记录每条数据的日志
            }
            log.info("读取特定工作表的Excel文件完成"); // 读取完成后记录日志
        } catch (Exception e) {
            log.error("读取特定工作表的Excel失败", e); // 记录错误日志
        }
    }
     /**
     * 使用模板填充数据并生成新的Excel文件
     * @param templateFilePath 模板文件路径
     * @param outputFilePath 输出文件路径
     * @param data 要填充的数据列表
     */
    public void fillTemplate(String templateFilePath, String outputFilePath, List<UserData> data) {
        try {
            log.info("开始使用模板填充数据: {}, 输出文件路径: {}", templateFilePath, outputFilePath); // 记录日志

            // 使用模板填充数据
            EasyExcel.write(outputFilePath)
                    .withTemplate(templateFilePath)
                    .sheet()
                    .doFill(data);

            log.info("模板填充数据完成"); // 填充完成后记录日志
        } catch (Exception e) {
            log.error("模板填充数据失败", e); // 记录错误日志
        }
    }
    
    /**
     * 下载文件并在失败时返回JSON
     * @param response HttpServletResponse 对象
     * @throws IOException 如果写入文件失败
     */
    public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
        try {
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");

            // 这里URLEncoder.encode可以防止中文乱码
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

            
            List<UserData> data = new ArrayList<>();
            for (int i = 1; i <= 100; i++) { // 假设我们有100条数据
                data.add(new UserData().setName("张三" + i).setAge(20 + i % 50));
            }

            EasyExcel.write(response.getOutputStream(), UserData.class).autoCloseStream(Boolean.FALSE).sheet("模板")
                    .doWrite(data);
        } catch (Exception e) {
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = Map.of("status", "failure", "message", "下载文件失败: " + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }
}
}

6. 创建控制器类

为了方便测试,我们可以创建一个简单的控制器类来调用服务类中的方法:

package com.example.demo.controller;

import com.example.demo.service.ExcelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Slf4j // 使用@Slf4j注解简化日志记录
@RestController
@RequestMapping("/excel")
public class ExcelController {

    @Autowired
    private ExcelService excelService; // 自动注入ExcelService
    /**
     * 文件上传
     * @param file 上传的文件
     * @return 成功消息
     * @throws IOException 如果读取文件失败
     */
    @PostMapping("upload")
    @ResponseBody
    public String upload(@RequestParam("file") MultipartFile file) throws IOException {
        InputStream inputStream = file.getInputStream();
        List<UserData> data = excelService.readAllSheets(inputStream);
        log.info("成功读取到 {} 条数据", data.size());
        return "success";
    }
    
    /**
     * 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel)
     *
     * @param response HttpServletResponse 对象
     * @throws IOException 如果写入文件失败
     */
    @GetMapping("downloadFailedUsingJson")
    public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
        excelService.downloadFailedUsingJson(response);
    }

    /**
     * 写入Excel文件
     * @param filePath 文件路径
     * @return 成功消息
     */
    @GetMapping("/write")
    public String writeExcel(@RequestParam String filePath) {
        log.info("准备写入Excel文件: {}", filePath); // 记录日志

        List<UserData> data = new ArrayList<>();
        for (int i = 1; i <= 500; i++) { // 假设我们有500条数据
            data.add(new UserData().setName("张三" + i).setAge(20 + i % 50));
        }

        excelService.writeExcel(filePath, data); // 调用写入方法
        log.info("写入Excel文件完成"); // 返回成功消息
        return "写入Excel成功"; // 返回成功消息
    }

    /**
     * 读取所有工作表的Excel文件
     * @param filePath 文件路径
     * @return 成功消息
     */
    @GetMapping("/read/allSheets")
    public String readAllSheets(@RequestParam String filePath) {
        log.info("准备读取所有工作表的Excel文件: {}", filePath); // 记录日志
        excelService.readAllSheets(filePath); // 调用读取方法
        log.info("读取所有工作表的Excel文件完成"); // 返回成功消息
        return "读取所有工作表的Excel成功"; // 返回成功消息
    }

    /**
     * 读取特定工作表的Excel文件
     * @param filePath 文件路径
     * @param sheetIndex 工作表索引(从0开始)
     * @return 成功消息
     */
    @GetMapping("/read/specificSheet")
    public String readSpecificSheet(@RequestParam String filePath, @RequestParam int sheetIndex) {
        log.info("准备读取特定工作表的Excel文件: {}, Sheet Index: {}", filePath, sheetIndex); // 记录日志
        excelService.readSpecificSheet(filePath, sheetIndex); // 调用读取方法
        log.info("读取特定工作表的Excel文件完成"); // 返回成功消息
        return "读取特定工作表的Excel成功"; // 返回成功消息
    }
    /**
     * 使用模板填充数据并生成新的Excel文件
     * @param templateFilePath 模板文件路径
     * @param outputFilePath 输出文件路径
     * @return 成功消息
     */
    @GetMapping("/fill/template")
    public String fillTemplate(@RequestParam String templateFilePath, @RequestParam String outputFilePath) {
        log.info("准备使用模板填充数据: {}, 输出文件路径: {}", templateFilePath, outputFilePath); // 记录日志

        List<UserData> data = new ArrayList<>();
        for (int i = 1; i <= 500; i++) { // 假设我们有500条数据
            data.add(new UserData().setName("张三" + i).setAge(20 + i % 50));
        }

        excelService.fillTemplate(templateFilePath, outputFilePath, data); // 调用模板填充方法
        log.info("模板填充数据完成"); // 返回成功消息
        return "模板填充数据成功"; // 返回成功消息
    }
}

7. 异常处理与日志记录

在实际应用中,建议增加更多的异常处理逻辑和日志记录,以便更好地调试和维护。我们已经在服务类中添加了基本的日志记录和异常处理机制。

全局异常处理

你可以在项目中添加全局异常处理器来捕获和处理未处理的异常:

package com.example.demo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import lombok.extern.slf4j.Slf4j;

@Slf4j // 使用@Slf4j注解简化日志记录
@ControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理所有异常
     * @param e 异常对象
     * @return 错误响应
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleException(Exception e) {
        log.error("发生错误: ", e); // 记录错误日志
        return new ResponseEntity<>("发生错误: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); // 返回错误消息
    }
}

8. 启动应用程序

确保你的主应用程序类正确配置:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.slf4j.Slf4j;

@SpringBootApplication
@Slf4j // 使用@Slf4j注解简化日志记录
public class DemoApplication {

    /**
     * 主函数,启动Spring Boot应用
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args); // 启动Spring Boot应用
        log.info("Spring Boot应用已启动"); // 记录启动日志
    }
}

9. 测试

启动应用程序后,你可以通过浏览器或Postman等工具访问以下URL来测试Excel的读写功能:

  • 写入Excel:http://localhost:8080/excel/write?filePath=/path/to/your/output.xlsx
  • 读取Excel:http://localhost:8080/excel/read?filePath=/path/to/your/input.xlsx

总结

以上是完整的Spring Boot集成EasyExcel的详细步骤和代码示例,包括详细的注释以及实现了当单个页面数据达到一定量时重新生成新的页面进行写入的功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2314823.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

obeaver 连接oracle 库 模式乱码

下载orai18n-12.1.0.2.0.jar 库--添加文件--把提前下载好的jar 随便放在一个文件夹下--添加文件选中&#xff0c;然后点击找到类&#xff0c; 选择类&#xff0c;确定即可正常 下载地址&#xff1a;https://download.csdn.net/download/weixin_42845364/88368302

ChatGPT 使用教程:深度探索AI常用功能技巧

文章目录 前言一、ChatGPT介绍1.1 人工智能与自然语言处理的发展1.2 ChatGPT 的诞生与意义 二、ChatGPT 基础入门2.1 注册与登录2.2 对话界面介绍2.3 基本提问方式 三、常用功能详解3.1 文本生成3.2 问题回答3.3 语言翻译3.4 代码生成与调试 四、高级使用技巧4.1 指令优化4.2 多…

[HUBUCTF 2022 新生赛]messy_traffic

下载附件 看到文件类型直接用wireshark打开&#xff0c;对MySQL协议进行追踪流&#xff0c;并没有什么发现&#xff0c;后面对NO.437发现有用信息&#xff0c;http追踪流 发现**system(‘cat passwd.txt’);**这里是在打开查看passwd.txt&#xff0c;密码是"SignUpForHUBU…

铁人三项(第五赛区)_2018_rop题解

先启动靶机连接看看。 直接ls&#xff0c;就给我输出句话&#xff0c;看来不能直接拿flag。 那走下流程。 查下位数和其他信息&#xff1a; 可以看到是32位的包&#xff0c;开了NX&#xff0c;但没开其他保护。 用ida32打开looklook。 主函数就是个这&#xff0c;看到了弹出的…

Compose 实践与探索六 —— 动画的流程控制与 Transition

1、Block 参数&#xff1a;监听每一帧 animateTo() 与 animateDecay() 中都有一个函数类型的 block 参数&#xff1a; suspend fun animateDecay(initialVelocity: T,animationSpec: DecayAnimationSpec<T>,block: (Animatable<T, V>.() -> Unit)? null): An…

虚拟机Contos7为啥不能被本机电脑访问?

1.查看防火墙是否开启 systemctl status firewalld.service 2.如果防火墙关闭就可以直接被访问 3.如果防火墙打开了我们需要开放端口(下面为防火墙一系列指令) # 关闭防火墙 systemctl stop firewalld.service# 打开防火墙 systemctl start firewalld.service# 关闭开启自启…

idea超级AI插件,让 AI 为 Java 工程师

引言​ 用户可在界面中直接通过输入自然语言的形式描述接口的需求&#xff0c;系统通过输入的需求自动分析关键的功能点有哪些&#xff0c;并对不确定方案的需求提供多种选择&#xff0c;以及对需求上下文进行补充&#xff0c;用户修改确定需求后&#xff0c;系统会根据需求设…

µCOS-III从入门到精通 第十四章(软件定时器)

参考教程&#xff1a;【正点原子】手把手教你学UCOS-III实时操作系统_哔哩哔哩_bilibili 一、软件定时器简介 1、定时器的概念与种类 &#xff08;1&#xff09;定时器的概念&#xff1a;从指定的时刻开始&#xff0c;经过一个指定时间&#xff0c;然后触发一个超时事件&…

MySQL数据库复杂的增删改查操作

在前面的文章中&#xff0c;我们主要学习了数据库的基础知识以及基本的增删改查的操作。接下去将以一个比较实际的公司数据库为例子&#xff0c;进行讲解一些较为复杂且现时需求的例子。 基础知识&#xff1a; 一文清晰梳理Mysql 数据库基础知识_字段变动如何梳理清楚-CSDN博…

KCD 北京站丨Volcano 邀您畅聊云原生智能调度技术与应用

AI与云原生技术正以前所未有的速度改变着我们的世界&#xff0c;而云原生技术则如同一座坚实的桥梁&#xff0c;连接着传统IT与现代化的数字世界。当AI与云原生相遇&#xff0c;它们相互赋能&#xff0c;相互促进&#xff0c;为开发者们打开了一个全新的技术宇宙。 3 月 15 日&…

BLEU评估指标

一、介绍 用于评估模型生成的句子和实际句子差异的指标&#xff0c;取值在[0,1]&#xff0c;匹配度高就距离1近&#xff0c;反之距离0近。这个指标计算代价小&#xff0c;容易理解&#xff0c;与语言无关&#xff0c;与人类评价结果高度相关。 BLEU主要基于n-gram匹配&#x…

高效自动化测试:打造Python+Requests+Pytest+Allure+YAML的接口测试框架

一、背景 在快节奏的开发周期中&#xff0c;如何确保接口质量&#xff1f;自动化测试是关键。通过构建标准化、可复用的测试框架&#xff0c;能显著提升测试效率与准确性&#xff0c;为项目质量保驾护航[1][7]。 二、目标 ✅ 核心目标&#xff1a; ● 实现快速、高效的接口测试…

BSides Vancouver: 2018 (Workshop)

BSides Vancouver: 2018 (Workshop) 来自 <https://www.vulnhub.com/entry/bsides-vancouver-2018-workshop,231/> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23…

rStar论文精读

论文简介 论文标题&#xff1a;《Mutual reasoning makes smaller LLMs stronger problem-solvers》 论文地址&#xff1a;https://arxiv.org/abs/2408.06195 录用会议&#xff1a;ICLR2025 背景与挑战 挑战1&#xff1a;在SLM中平衡exploration与exploitation。一些方法有很…

247g 的工业级电调,如何让无人机飞得更 “聪明“?——STONE 200A-M 深度测评

一、轻量化设计背后的技术取舍 当拿到 STONE 200A-M 时&#xff0c;247g 的重量让人意外 —— 这个接近传统 200A 电调 70% 的重量&#xff0c;源自 1205624.5mm 的紧凑结构&#xff08;0.1mm 公差控制&#xff09;。实测装机显示&#xff0c;相比同规格产品&#xff0c;其体积…

Node.js:快速启动你的第一个Web服务器

Node.js 全面入门指南 文章目录 Node.js 全面入门指南一 安装Node.js1. Windows2. MacOS/Linux 二 配置开发环境1. VSCode集成 三 第一个Node.js程序1. 创建你的第一个Node.js程序 四 使用Express框架1. 快速搭建服务器 一 安装Node.js 1. Windows 以下是Windows环境下Node.j…

自定义日志回调函数实现第三方库日志集成:从理论到实战

一、应用场景与痛点分析 在开发过程中&#xff0c;我们经常会遇到以下场景&#xff1a; 日志格式统一&#xff1a;第三方库使用自己的日志格式&#xff0c;导致系统日志混杂&#xff0c;难以统一管理和分析。日志分级过滤&#xff1a;需要动态调整第三方库的日志输出级别&…

Linux练级宝典->任务管理和守护进程

任务管理 进程组概念 每个进程除了进程ID以外&#xff0c;还有一个进程组&#xff0c;进程组就是一个或多个进程的集合 同一个进程组&#xff0c;代表着他们是共同作业的&#xff0c;可以接收同一个终端的各种信号&#xff0c;进程组也有其唯一的进程组号。还有一个组长进程&a…

C语言:计算并输出三个整数的最大值 并对三个数排序

这是《C语言程序设计》73页的思考题。下面分享自己的思路和代码 思路&#xff1a; 代码&#xff1a; #include <stdio.h> int main() {int a,b,c,max,min,mid ; //设置大中小的数分别为max&#xff0c;mid&#xff0c;min&#xff0c;abc为输入的三个数printf("ple…

工具(十二):Java导出MySQL数据库表结构信息到excel

一、背景 遇到需求&#xff1a;将指定数据库表设计&#xff0c;统一导出到一个Excel中&#xff0c;存档查看。 如果一个一个弄&#xff0c;很复杂&#xff0c;耗时长。 二、写一个工具导出下 废话少絮&#xff0c;上码&#xff1a; 2.1 pom导入 <dependency><grou…