SpringBoot+Vue实现Excel文档导入和导出

1.准备工作

1.1.前端程序

在前端首先加上批量导出的按钮,如下

html 复制代码
<el-button size="small" type="warning" plain @click="exportData">
批量导出
</el-button>

在添加了点击事件之后,在methods中要与之对应的添加上exportData的方法,其中multipleSelection是复选框中勾选后用户的id,下面的代码逻辑为,当我没有勾选复选框的时候(也就是multipleSelection的长度为0时)就执行导出功能

javascript 复制代码
//批量导出
    exportData(){
      //没有选择行的时候全部导出,或者根据搜索条件导出
      if(!this.multipleSelection.length){
       
      }
    },

1.2.后端程序

前端基本架构写好了,接下来我们开始写后端的接口,如下

  • 接口的参数有两个,一个是用户的名字,一个是response对象(为了获取输出流,将Excel写回浏览器)
  • 借助hutool工具包提供的ExcelUtil类获取一个ExcelWrite对象(别忘了导入hutool的jar包)
XML 复制代码
<!--hutool工具包-->
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.7.16</version>
</dependency>
  • 执行查询所有用户,将结果添加到write对象中
  • 在获取了输出流之后,将write对象写到流里
java 复制代码
     /**
     * 批量导出
     * @param name
     */
    @GetMapping("/export")
    public void exportData(@RequestParam(required = false) String name, HttpServletResponse response) throws IOException {
        ExcelWriter writer = ExcelUtil.getWriter(true);
        List<User> list = new ArrayList<>();
        //第一种:全部导出(当name为空的时候执行,也就是说前端没有勾选复选框时,全部导出)
        if(StringUtils.isBlank(name)){
            //查询所有用户
            list = userService.list();
        }
        writer.write(list,true);
        //获取输出流
        ServletOutputStream outputStream = response.getOutputStream();
        //将excel写入到输出流里,并设置用完流之后就关闭
        writer.flush(outputStream,true);
    }

2.完善工作

这个时候,后端的程序也基本写好了,现在返回前端来写请求,我的请求之中添加了token,因为之前添加过jwt验证,如果没有token的话,就不能访问,但是之前在axios的请求头中都添加了token,而这个请求是js中的,所以要自己手动在请求路径中添加一个token(而之前后端程序中写了,如果请求头里没有token,那么后台会自己在请求路径中寻找token)

javascript 复制代码
  //批量导出
    exportData(){
      //没有选择行的时候全部导出,或者根据搜索条件导出
      if(!this.multipleSelection.length){
        window.open('http://localhost:8082/user/export?token='+this.user.token)
      }
    },

3.测试

前后端框架基本写好了,接下来进行测试,启动前后端工程后,点击批量导出

可以看到,报了500的错误,那就表明是后台代码写的有问题,返回后端查看,发现控制台报了以下错误

这个错误比较经典,在对Excel进行处理的时候我们必须引入一个叫POI-OOXML的依赖,如下:

XML 复制代码
 <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
 </dependency>

接下来,进行测试,发现下载的文件是zip类型的,这与Excel表格格式后缀xlsx不符,所以我们还要在后台添加导出的文件格式

java 复制代码
response.setContentType("application/vnd.openxmlformatsofficedocument.spreadsheetml.sheet;charset=utf-8");
response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode("用户信息表","UTF-8")+".xlsx");
  • setContentType设置了HTTP响应的Content-Typeapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheet,这表示响应的内容是一个Excel文件,即OpenXML格式的电子表格。charset=utf-8指定了字符编码为UTF-8。
  • setHeader设置了HTTP响应的Content-Disposition头,其值为attachment,表示响应的内容是附件,浏览器会提示用户下载该文件。filename=后面跟的是附件的默认文件名,这里通过URLEncoder.encode方法对"用户信息表"进行了URL编码,以确保文件名中的非ASCII字符在HTTP头中正确传输。编码后的字符串加上.xlsx后缀,构成了完整的文件名。

4.第二次测试

加上了响应格式之后,我们再来一次测试,如下:

可以看到,该文件名字已经被修改成"用户信息表",并且格式也不是之前的.zip,而是Excel的格式,打开文件,如下:

可以看到表头对应的是实体类的字段名,那么如何将表头换成中文呢?
只需要在实体类的字段上加上@Alias注解并设置别名即可**(注意:这个@Alias注解是hutool包下的,不是mybatis包下的)**,如下:

加上注解之后,再进行测试,Excel表头就会被修改成指定的名称

到目前为止,Excel导出的功能就完成了,但是还要修改一下业务逻辑

  • 当我不进行模糊查询的时候,点击批量导出,则要导出全部的数据
  • 当我进行模糊查询的时候,点击批量导出,则要导出查询出来的全部数据

所以我要给代码修改一下逻辑,如下:

java 复制代码
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.isNotBlank(name),User::getName,name);

list = userService.list(wrapper);

添加一个模糊查询,并设置一个条件,只有当name的值不为空,才会进行模糊查询,否则就不会添加模糊查询的条件,这样就完成了上面提出的业务要求

5.完善后的前后端代码

java 复制代码
@GetMapping("/export")
    public void exportData(@RequestParam(required = false) String name, HttpServletResponse response) throws IOException {
        ExcelWriter writer = ExcelUtil.getWriter(true);
        List<User> list = new ArrayList<>();
        //第一种:全部导出(当name为空的时候执行,也就是说前端没有勾选复选框时,全部导出)
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(name),User::getName,name);

        //if(StringUtils.isBlank(name)){
            //查询所有用户
            list = userService.list(wrapper);
        //}
        writer.write(list,true);

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode("用户信息表","UTF-8")+".xlsx");
        //获取输出流
        ServletOutputStream outputStream = response.getOutputStream();
        //将excel写入到输出流里,并设置用完流之后就关闭
        writer.flush(outputStream,true);
        //关闭write
        writer.close();
    }

前端添加的代码是要给请求添加一个name属性,这样后台才能接收到name,如下:

javascript 复制代码
 //批量导出
    exportData(){
      //没有选择行的时候全部导出,或者根据搜索条件导出
      if(!this.multipleSelection.length){
        window.open('http://localhost:8082/user/export?token='+this.user.token+'&name='+this.name)
      }
    },

测试如下:

首先查询姓王的用户,查询结果如下:

然后再导出,结果如下:

自此,初步功能已经完善

6.勾选复选框之后进行导出

之前完成的是,不勾选复选框,然后进行全部数据的导出,和进行模糊搜索之后,将搜索出的数据进行全部导出
整体思路

  • 要想勾选复选框之后对指定的数据进行导出,那么在勾选复选框之后就要把该用户的id传给后端,由于可以选择多个用户,那么前端传的数据应该是一个数组类型,但是GET方法传不了数组,所以我们可以将其转换成字符串类型,然后后端用String类型接收,然后再将字符串转换成List集合进行查询(为什么要转换成集合,因为mybatis-plus中的条件构造器in需要一个list类型的参数)
    在前端添加id的请求参数,如下,第一行程序意思是,将前端复选框勾选的用户的id数组 转换成字符串,并且以逗号分隔,类似于"1","2","3"
javascript 复制代码
let idStr = this.multipleSelection.join(',');
window.open('http://localhost:8082/user/export?token='+this.user.token+'&ids=' + idStr)

后端用String类型的参数即可接收,如下:

java 复制代码
  @RequestParam(required = false) String ids

接下来的工作就是将字符串类型的参数转换成list集合,并将其添加到条件查询器里,如下:

java 复制代码
//将字符串按逗号分割成数组
String[] id = ids.split(",");
//使用Stream API 将字符串数组转换为整数集合
List<Integer> userId = Arrays.stream(id).map(Integer::valueOf).collect(Collectors.toList());
wrapper.in(StringUtils.isNotBlank(ids),User::getId,userId);

全部代码

后端:

java 复制代码
    /**
     * 批量导出
     * @param name
     */
    @GetMapping("/export")
    public void exportData(@RequestParam(required = false) String name,
                           @RequestParam(required = false) String ids,
                           HttpServletResponse response) throws IOException {
        ExcelWriter writer = ExcelUtil.getWriter(true);
        List<User> list = new ArrayList<>();
        //第一种:全部导出(当name为空的时候执行,也就是说前端没有勾选复选框时,全部导出)
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

        //如果ids不为空(也就是我勾选了复选框),就执行将传进来的ids字符串转换成数组类型的操作然后导出勾选的用户数据,否则就执行全部导出
        if(StringUtils.isNotBlank(ids)){
            //将字符串按逗号分割成数组
            String[] id = ids.split(",");
            // 使用 Stream API 将字符串数组转换为整数集合
            List<Integer> userId = Arrays.stream(id).map(Integer::valueOf).collect(Collectors.toList());
            wrapper.in(StringUtils.isNotBlank(ids),User::getId,userId);

        }else {
            wrapper.like(StringUtils.isNotBlank(name),User::getName,name);
        }
        list = userService.list(wrapper);
        writer.write(list,true);

        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode("用户信息表","UTF-8")+".xlsx");
        //获取输出流
        ServletOutputStream outputStream = response.getOutputStream();
        //将excel写入到输出流里,并设置用完流之后就关闭
        writer.flush(outputStream,true);
        //关闭write
        writer.close();
    }

前端:

javascript 复制代码
    //批量导出
    exportData(){
      //没有选择行的时候全部导出,或者根据搜索条件导出
      if(!this.multipleSelection.length){
        window.open('http://localhost:8082/user/export?token='+this.user.token+'&name=' + this.name)
      }else {
        //不加","也可以,join默认会加上
        let idStr = this.multipleSelection.join(',');
        window.open('http://localhost:8082/user/export?token='+this.user.token+'&ids=' + idStr)
      }
    },

7.批量导入功能实现

  • 由于Excel文件导入和文件上传本质一样,所以后端接口的参数也要一个MultipartFile
  • 同样的,Excel文件导入也要借助ExcelUtil,其中的getReader方法需要一个Input流作为参数,而MultipartFile类型能获取InputStream
  • 获取到reader之后,readAll方法将Excel文件中的数据读取为一个User对象的列表。User.class是Java反射中用来指定类的。
  • 获取到Excel表中的User类型的数据集合,再调用mybatisplus中的方法,将数据写入数据库
  • 其中这个ServiceException是自定义的异常

后端:

java 复制代码
    @PostMapping("/import")
    public Result importData(MultipartFile file) throws IOException {
        try {
            //获取inputStream流
            InputStream inputStream  = file.getInputStream();
            ExcelReader reader = ExcelUtil.getReader(inputStream);
            List<User> userList = reader.readAll(User.class);
            userService.saveBatch(userList);
        } catch (Exception e) {
            throw new ServiceException("文件上传失败");
        }
        return Result.success();
    }

前端:

来看看上传文件的前端代码,如下:

  • 文件上传是一个特定的代码,其中Element-UI规定了以下格式,其中有几个属性需要了解
  • action 后面跟请求路径
  • :headers 后面跟要在这个请求的请求头里添加的东西
  • :show-file-list 表示文件上传成功后要不要显示上传成功的文件的列表,false表示关闭,true表示开启
  • :on-success 后面跟文件上传成功之后的事件,handleImport事件如下:
javascript 复制代码
    //批量导入
    handleImport(res,file,fileList){
      console.log(res)
      this.load(1)
      if (res.code === '200'){
        this.$message.success("上传成功")
      }else {
        this.$message.error(res.msg)
      }
    },
html 复制代码
<el-upload
    style="display: inline-block;margin-left: 10px"
    action="http://localhost:8082/user/import"
    :headers="{token:user.token}"
    :show-file-list="false"
    :on-success="handleImport">
    <el-button size="small" plain type="primary">文件导入</el-button>
</el-upload>

到此为止,该模块的功能全部实现>_<

相关推荐
SuperEugene8 小时前
Axios 接口请求规范实战:请求参数 / 响应处理 / 异常兜底,避坑中后台 API 调用混乱|API 与异步请求规范篇
开发语言·前端·javascript·vue.js·前端框架·axios
cjy0001119 小时前
springboot的 nacos 配置获取不到导致启动失败及日志不输出问题
java·spring boot·后端
sheji341610 小时前
【开题答辩全过程】以 基于springboot的校园失物招领系统为例,包含答辩的问题和答案
java·spring boot·后端
英俊潇洒美少年10 小时前
vue如何实现react useDeferredvalue和useTransition的效果
前端·vue.js·react.js
英俊潇洒美少年10 小时前
ref 底层到底是怎么变成响应式的?
vue.js
英俊潇洒美少年11 小时前
react19和vue3的优缺点 对比
前端·javascript·vue.js·react.js
程序员阿伦12 小时前
璋㈤鏈虹殑Java澶у巶闈㈣瘯璁帮細浠嶴pring Boot鍒癒ubernetes锛�3杞湡棰樺叏瑙f瀽锛�
spring boot·redis·kubernetes·aigc·java闈㈣瘯·寰湇鍔�·鐢靛晢绉掓潃
wuyikeer12 小时前
Spring BOOT 启动参数
java·spring boot·后端
多看书少吃饭12 小时前
Vue + Java + Python 打造企业级 AI 知识库与任务分发系统(RAG架构全解析)
java·vue.js·笔记
zdl68613 小时前
springboot集成onlyoffice(部署+开发)
java·spring boot·后端