Spring Boot项目如何优雅实现Excel导入与导出功能

目录
  • 背景
  • EasyExcel 问题
  • 分析与解决
  • Spring Boot Excel 导入与导出
    • 依赖引入
    • Excel 导入
      • 基本导入功能
      • 进阶导入功能
    • Excel 导出
  • Excel 导入参数校验
    • 开启校验
    • 校验规则定义
      • Bean Validation 定义校验规则
      • ExcelValidator 接口定义校验规则
    • 校验结果接收
      • 异常捕获接收校验结果
      • controller 方法参数接收校验结果
  • 总结

背景

Excel 导入与导出是项目中经常用到的功能,在 Java 中常用 poi 实现 Excel 的导入与导出。由于 poi 占用内存较大,在高并发下很容易发生 OOM 或者频繁 fullgc,阿里基于 poi 开源了 EasyExcel 项目。

除了节约内存,EasyExcel 还简化了 API,通过注解映射 Excel 单元格与对象字段之间的关系,简单的几行代码就能搞定复杂的导入导出功能了。

EasyExcel 问题

看似一切美好,不过经常做 Excel 导入与导出就会发现,EasyExcel 还是没那么完美的。

首先,导入与导出 Excel 本质是上将 Excel 文件内容与 Java 对象之间做一个映射,EasyExcel 做的只是在这两者之间转换。如果项目中的 Excel 导入与导出功能比较多,会产生大量的样板式代码,使用体验类似于 JDBC。

另外,导入往往还伴随着校验,这是 EasyExcel 没有支持的功能。如果需要校验,要么写代码手动判断,要么调用 Java Validation 规范 定义的 API 判断,这又会产生大量样板式代码。

而且,当前 spring boot 已经成了必备的 Java 开发框架,easyexcel 也没有进行整合。

分析与解决

导入与导出通常发生在 Web 环境,对于 Spring MVC 来说,可以将请求信息转换为任意类型的 contoller 方法参数,将 controller 方法返回值转换为客户端支持的内容。

如果能够使用自定义的 controller 方法参数接收 Excel 文件内容,将 controller 方法返回值转换为 Excel 文件响应,可以直接消除 Excel 导入与导出时的样板式代码。

另外在将请求内容转换为 controller 方法参数时还可以加入自定义的校验逻辑。

由于 Excel 导入与导出样板式代码、校验问题与具体的业务逻辑无关,可以单独抽象出来,我这里在 EasyExcel 的基础上封装了一个 easyexcel-spring-boot-starter 的项目,大大降低了 EasyExcel 上手的门槛,对用户来说只需要使用 EasyExcel 定义的注解提供映射关系就可以了,适用于简单场景的导入导出。

项目代码已上传 github easyexcel-spring-boot-starter 仓库,点击链接即可查阅。下面就来看看怎样使用吧。

Spring Boot Excel 导入与导出

依赖引入

首先需要引入依赖,坐标如下。

<dependency>
    <groupId>com.zzuhkp</groupId>
    <artifactId>easyexcel-spring-boot-starter</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

不过很不幸的是目前还没传至中央仓库,需要的小伙伴可自行上传到私有仓库或直接把代码嵌入自己的项目。

Excel 导入

首先看下要导入的 Excel 内容吧。

为了接收 Excel 文件内容,我们需要定义一个对应的 Model 类。

@Data
public class DemoData {
    @ExcelProperty(index = 0)
    private Integer integer;

    @ExcelProperty(index = 1)
    private String string;

    @ExcelProperty(index = 2)
    private Date date;
}

基本导入功能

然后使用 List<T> 参数接收即可。

@PostMapping("/list/obj")
public List<DemoData> listObj(@ExcelParam List<DemoData> list) {
    return list;
}

注意参数前添加了 @ExcelParam 注解,用来标识 Excel 文件参数。这样,一个导入功能实现了,是不是很简单呢?

默认情况下接收名称为 file 的表单字段作为 Excel 文件,如果不满足还可以修改。

@ExcelParam(value = "file", required = true)

进阶导入功能

有时候,我们可能比较关心对象对应 Excel 的元数据,例如这个对象是第几行记录产生的,这个对象的字段对应 Excel 第几列,这个时候我们可以使用 ReadRows<T> 参数接收 Excel。

@PostMapping("/list/rows")
public ReadRows<DemoData> readRows(@ExcelParam ReadRows<DemoData> readRows) {
    return readRows;
}

ReadRows 使用两个字段记录行映射关系与列映射关系。

public class ReadRows<T> {

    private ExcelReadHeadProperty excelReadHeadProperty;

    private List<ReadRow<T>> rows;
}

ExcelReadHeadProperty 是 EasyExcel 自带的类,表示列映射关系的元数据。ReadRow 是框架自定义的类,表示行映射关系的元数据。

看下 ReadRow 定义吧。

public class ReadRow<T> {

    // 行索引,从 0 开始
    private final Integer rowIndex;

    // 行记录对应对象
    private final T data;
}

使用 ExcelReadHeadProperty 获取字段对应列索引的示例代码如下。

// 对象字段名称 -> 从 0 开始的列索引
Map<String, Integer> fieldColumnIndexMap = readRows.getExcelReadHeadProperty().getHeadMap().values()
        .stream().collect(Collectors.toMap(Head::getFieldName, Head::getColumnIndex));

Excel 导出

这里对 Excel 的导出进行了简单的支持。将 List<T> 定义为 controller 方法返回值即可。

@ExcelResponse
@GetMapping("/list/download")
public List<DemoData> downloadList() {
    return Arrays.asList(new DemoData(1, "hello", new Date()), new DemoData(2, "excel", new Date()));
}

需要注意的是使用 @ExcelResponse 注解表示响应内容为 Excel 文件。默认情况,下载的文件名称为 default.xlxs,写入到名称为 Sheet1 的工作表中。如果不满足需求可以修改。

@ExcelResponse(fileName = "测试文件", sheetName = "工作表1")

Excel 导入参数校验

参数校验是 Excel 导入常用的功能,这里进行了强有力的支持,使用体验如原生 spring boot 校验般顺滑。

开启校验

与 spring boot 原生使用方式一样,将 @Validated@Valid 注解添加到 @ExcelParam 参数上即可。

@PostMapping("/list/obj")
public List<DemoData> listObj(@ExcelParam @Validated List<DemoData> list) {
    return list;
}

校验规则定义

Bean Validation 定义校验规则

默认情况下框架使用 JSR-303 Bean Validation 规范定义的校验注解校验,需要手动引入 spring-boot-starter-validation,可通过设置环境变量 easyexcel.validator.default.enable=false 关闭。

@Data
public class DemoData {
    @NotNull(message = "参数不能为空")
    private Integer integer;

    private String string;

    private Date date;
}

另外还可以自定义注解对对象校验。

... 省略其他元注解
@Constraint(validatedBy = {DemoDataValid.DemoDataValidator.class})
public @interface DemoDataValid {
		... 省略注解属性

    class DemoDataValidator implements ConstraintValidator<DemoDataValid, DemoData> {

        @Override
        public boolean isValid(DemoData value, ConstraintValidatorContext context) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate("测试对象校验").addConstraintViolation();
            return false;
        }
    }
}
@DemoDataValid
public class DemoData {
    ... 省略属性
}

ExcelValidator 接口定义校验规则

Bean Validation 注解只能校验单个字段或对象,如果需要对所有的对象进行校验,可以实现框架定义的 ExcelValidator 接口,然后将实现定义为 Spring Bean。

这个接口定义如下。

public interface ExcelValidator<T> {
    ExcelValidErrors validate(ReadRows<T> readRows);
}

ExcelValidErrors 用于接收校验的错误信息,分别使用接口 ExcelValidObjectErrorExcelValidFieldError 接口定义行错误信息和单元格错误信息。

public class ExcelValidErrors {
	// 行错误信息或单元格错误信息列表
    private final List<ExcelValidObjectError> errors;
}

public interface ExcelValidObjectError {
    // 获取行号,从 1 开始
    Integer getRow();

    // 获取错误消息
    String getMessage();
}

public interface ExcelValidFieldError extends ExcelValidObjectError {
    // 获取列,从 1 开始
    Integer getColumn();
}

例如,如果需要对所有的 DemoData 校验 integer 字段的值不能重复,可以使用如下的代码。

@Component
public class CustomExcelValidator implements ExcelValidator<DemoData> {
    @Override
    public ExcelValidErrors validate(ReadRows<DemoData> readRows) {
        ExcelValidErrors errors = new ExcelValidErrors();

        Map<Integer, List<ReadRow<DemoData>>> group = readRows.getRows().stream()
                .collect(Collectors.groupingBy(item -> item.getData().getInteger()));

        for (Map.Entry<Integer, List<ReadRow<DemoData>>> entry : group.entrySet()) {
            if (entry.getValue().size() > 1) {
                for (ReadRow<DemoData> readRow : entry.getValue()) {
                    errors.addError(new DefaultExcelObjectError(readRow.getRowIndex() + 1, "参数重复"));
                }
            }
        }
        return errors;
    }
}

校验结果接收

与 Spring MVC 设计类似,这里也提供了两种接收校验结果的方式。

异常捕获接收校验结果

开启校验后,如果校验结果中包含错误,会将错误信息封装到 ExcelValidException,并抛出异常,可以通过全局异常捕获的方式收集错误信息。

@RestControllerAdvice
public class GlobalExceptionControllerAdvice {
    @ExceptionHandler(ExcelValidException.class)
    public String handleException(ExcelValidException e) {
        ExcelValidErrors errors = e.getErrors();
        return JSON.toJSONString(errors);
    }
}

controller 方法参数接收校验结果

如果不想通过异常捕获的方式接收校验的错误信息,还可以将错误信息添加到 @ExcelParam 参数的后面,示例代码如下。

@PostMapping("/list/obj")
public List<DemoData> listObj(@ExcelParam @Validated List<DemoData> list, ExcelValidErrors errors) {
    if (errors.hasErrors()) {
        String messages = errors.getAllErrors().stream().map(ExcelValidObjectError::getMessage).collect(Collectors.joining(" | "));
        throw new RuntimeException("发现异常:" + messages);
    }
    return list;
}

总结

easyexcel-spring-boot-starter 综合应用了前面文章介绍的各种 Spring 知识,代码量并不大,对实现感兴趣的小伙伴可自行查阅代码。由于这个框架是把 Excel 中所有的行数据收集到内存,因此只适合一些比较简单的场景。

到此这篇关于Spring Boot项目如何优雅实现Excel导入与导出功能的文章就介绍到这了,更多相关SpringBoot实现Excel导入导出内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot中EasyExcel实现Excel文件的导入导出

    前言 在我们日常的开发过程中经常会使用Excel文件的形式来批量地上传下载系统数据,我们最常用的工具是Apache poi,但是如果数据到底上百万时,将会造成内存溢出的问题,那么我们怎么去实现百万数据批量导入导出. 正文 Easyexcel Easyexcel 是阿里巴巴的开源项目,用来优化Excel文件处理过程: poi消耗内存严重:Java解析.生成Excel比较有名的框架有Apache poi.jxl.但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的

  • Springboot实现导入导出Excel的方法

    目录 一.添加poi的maven依赖 二.自定义注解(Excel属性标题.位置等) 三.CustomExcelUtils编写 四.定义导出实体类 五.Controller层代码编写 一.添加poi的maven依赖 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.13</version> </d

  • SpringBoot整合EasyExcel实现文件导入导出

    准备工作 注意:点击查看官网Demo 1. 引入pom依赖 <!--easyExcel--> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> </dependency> 2. 实现功能 结合Vue前端,实现浏览器页面直接导出日志文件 实现文件的导入 Excel文件下载 3. 日志实体类 实体类里有自定义转换器:用于

  • Spring Boot项目如何优雅实现Excel导入与导出功能

    目录 背景 EasyExcel 问题 分析与解决 Spring Boot Excel 导入与导出 依赖引入 Excel 导入 基本导入功能 进阶导入功能 Excel 导出 Excel 导入参数校验 开启校验 校验规则定义 Bean Validation 定义校验规则 ExcelValidator 接口定义校验规则 校验结果接收 异常捕获接收校验结果 controller 方法参数接收校验结果 总结 背景 Excel 导入与导出是项目中经常用到的功能,在 Java 中常用 poi 实现 Excel

  • spring boot项目导入依赖后代码报错问题的解决方法

    代码截图如图所示(由于本人问题已经解决,没来得及截图,所以在网上找了一张图片) ​ 针对图中所示的情况,可参考一下解决方案: 方案一: 在 Idea 导入 Spring Boot 项目代码报红,试过更改maven配置,maven clean操作,执行-U idea:idea等命令还是提示:cannot resolve symbol 'SpringBootApplication' .我最终解决方法是导入要导入项目的pom.xml文件,而不是导入现有项目解决.选择pom.xml后会弹出提示框,选择a

  • Spring Boot 项目搭建教程及注解

    Spring Boot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域成为领导者 特点: 1. 创建独立的Spring应用程序 2. 嵌入的Tomcat,无需部署WAR文件 3. 简化Maven配置 4. 自动配置Spring 5. 提供生产就绪型功能,如指标,健康检查和外部配置

  • Spring Boot 项目创建的详细步骤(图文)

    一. 简单介绍一下Spring Boot 世界惯例,在学习一个框架之前,我们需要了解一下这个框架的来历. 下面我们引用一下百度百科的解释. Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者 Spring Boo

  • 5分钟快速创建spring boot项目的完整步骤

    前言 上一篇博客说了如何创建spring boot项目,但是有些同学会觉得有点麻烦,有没有什么快速学会能快速创建spring boot项目的方法,答案是肯定的.接下来我们就一起来快速创建一个spring boot项目并让它跑起来. 我们今天用两种方式创建,分别是在线创建和使用IntelliJ IDEA创建. 1 在线创建 1.1 在浏览器中打开https://start.spring.io/,我们看到的是如下图所示: 1.2 Project选择Maven Project,Language选择Ja

  • 创建Spring Boot项目的几种方式总结(推荐)

    一.我们可以使用Spring Initializr来创建SpringBoot项目. Spring Initializr从本质上来说就是一个Web应用程序,它能为你生成Spring Boot项目结构.虽然不能生成应用程序代码,但它能为你提供一个基本的项目结构,以及一个用于构建代码的Maven或Gradle构建说明文件.你只需要写应用程序的代码就好了. Spring Initializr有几种用法. 1.通过Web界面使用. 2.通过Spring Tool Suite使用. 3.通过IntelliJ

  • 只需两步实现Eclipse+Maven快速构建第一个Spring Boot项目

    随着使用Spring进行开发的个人和企业越来越多,Spring从一个单一简介的框架变成了一个大而全的开源软件,最直观的变化就是Spring需要引入的配置也越来越多.配置繁琐,容易出错,让人无比头疼,简化Spring配置简直可以说是民心所向. Spring Boot是由Pivotal团队提供的一个基于Java的全新的开源框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.如今,Spring Boot逐渐成为快

  • spring boot项目fat jar瘦身的实现

    一.项目背景 spring cloud构建spring boot项目,精细化各项目的层次,达到降低耦合度的目的,项目间基于restful通信. 在对项目打包过程中,使用spring-boot-maven-plugin插件打包,生成的是fat jar,解压该jar包,会发现项目依赖的jar包存放于BOOT-INF下的lib文件夹中,分析多个子项目后会发现,相同的jar包占绝大多数,然后每次部署于线上环境,各系统的共同jar在服务器上其实是重复搁置的,因此自然会想到是否有方法将共同的jar包,或是不

  • 基于IDEA,Eclipse搭建Spring Boot项目过程图解

    如何创建一个Spring Boot项目?这里使用maven来进行依赖管理,根据常用的IDE,可以使用IDEA.Eclipse.或者访问官方网站搭建. 项目搭建环境准备 JDK:1.8MAVEN:3.6.3 使用IDEA搭建Spring Boot项目 打开IDEA,选择File -- > New --> Project ,然后选择Spring Initializr,点击Next 输入Group --> Artifact --> Next,其他可以不用修改. 选择Spring Boot

  • 详解Spring Boot最新版优雅停机的方法

    什么是优雅停机 先来一段简单的代码,如下: @RestController public class DemoController { @GetMapping("/demo") public String demo() throws InterruptedException { // 模拟业务耗时处理流程 Thread.sleep(20 * 1000L); return "hello"; } } 当我们流量请求到此接口执行业务逻辑的时候,若服务端此时执行关机 (ki

随机推荐