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


    1.准备工作

    1.1.前端程序

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

    1. <el-button size="small" type="warning" plain @click="exportData">
    2. 批量导出
    3. el-button>

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

    1. //批量导出
    2. exportData(){
    3. //没有选择行的时候全部导出,或者根据搜索条件导出
    4. if(!this.multipleSelection.length){
    5. }
    6. },

     1.2.后端程序

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

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

    2.完善工作

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

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

    3.测试

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

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

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

    1. <dependency>
    2. <groupId>org.apache.poigroupId>
    3. <artifactId>poi-ooxmlartifactId>
    4. <version>5.2.3version>
    5. dependency>

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

    1. response.setContentType("application/vnd.openxmlformatsofficedocument.spreadsheetml.sheet;charset=utf-8");
    2. 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导出的功能就完成了,但是还要修改一下业务逻辑

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

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

    1. LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
    2. wrapper.like(StringUtils.isNotBlank(name),User::getName,name);
    3. list = userService.list(wrapper);

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

     5.完善后的前后端代码

    1. @GetMapping("/export")
    2. public void exportData(@RequestParam(required = false) String name, HttpServletResponse response) throws IOException {
    3. ExcelWriter writer = ExcelUtil.getWriter(true);
    4. List list = new ArrayList<>();
    5. //第一种:全部导出(当name为空的时候执行,也就是说前端没有勾选复选框时,全部导出)
    6. LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
    7. wrapper.like(StringUtils.isNotBlank(name),User::getName,name);
    8. //if(StringUtils.isBlank(name)){
    9. //查询所有用户
    10. list = userService.list(wrapper);
    11. //}
    12. writer.write(list,true);
    13. response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
    14. response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode("用户信息表","UTF-8")+".xlsx");
    15. //获取输出流
    16. ServletOutputStream outputStream = response.getOutputStream();
    17. //将excel写入到输出流里,并设置用完流之后就关闭
    18. writer.flush(outputStream,true);
    19. //关闭write
    20. writer.close();
    21. }

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

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

     测试如下:

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

     然后再导出,结果如下:

    自此,初步功能已经完善

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

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

    整体思路

    • 要想勾选复选框之后对指定的数据进行导出,那么在勾选复选框之后就要把该用户的id传给后端,由于可以选择多个用户,那么前端传的数据应该是一个数组类型,但是GET方法传不了数组,所以我们可以将其转换成字符串类型,然后后端用String类型接收,然后再将字符串转换成List集合进行查询(为什么要转换成集合,因为mybatis-plus中的条件构造器in需要一个list类型的参数) 

    在前端添加id的请求参数,如下,第一行程序意思是,将前端复选框勾选的用户的id数组转换成字符串,并且以逗号分隔,类似于"1","2","3"

    1. let idStr = this.multipleSelection.join(',');
    2. window.open('http://localhost:8082/user/export?token='+this.user.token+'&ids=' + idStr)

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

      @RequestParam(required = false) String ids

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

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

    全部代码

    后端:

    1. /**
    2. * 批量导出
    3. * @param name
    4. */
    5. @GetMapping("/export")
    6. public void exportData(@RequestParam(required = false) String name,
    7. @RequestParam(required = false) String ids,
    8. HttpServletResponse response) throws IOException {
    9. ExcelWriter writer = ExcelUtil.getWriter(true);
    10. List list = new ArrayList<>();
    11. //第一种:全部导出(当name为空的时候执行,也就是说前端没有勾选复选框时,全部导出)
    12. LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
    13. //如果ids不为空(也就是我勾选了复选框),就执行将传进来的ids字符串转换成数组类型的操作然后导出勾选的用户数据,否则就执行全部导出
    14. if(StringUtils.isNotBlank(ids)){
    15. //将字符串按逗号分割成数组
    16. String[] id = ids.split(",");
    17. // 使用 Stream API 将字符串数组转换为整数集合
    18. List userId = Arrays.stream(id).map(Integer::valueOf).collect(Collectors.toList());
    19. wrapper.in(StringUtils.isNotBlank(ids),User::getId,userId);
    20. }else {
    21. wrapper.like(StringUtils.isNotBlank(name),User::getName,name);
    22. }
    23. list = userService.list(wrapper);
    24. writer.write(list,true);
    25. response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
    26. response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode("用户信息表","UTF-8")+".xlsx");
    27. //获取输出流
    28. ServletOutputStream outputStream = response.getOutputStream();
    29. //将excel写入到输出流里,并设置用完流之后就关闭
    30. writer.flush(outputStream,true);
    31. //关闭write
    32. writer.close();
    33. }

    前端: 

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

    7.批量导入功能实现

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

    后端: 

    1. @PostMapping("/import")
    2. public Result importData(MultipartFile file) throws IOException {
    3. try {
    4. //获取inputStream流
    5. InputStream inputStream = file.getInputStream();
    6. ExcelReader reader = ExcelUtil.getReader(inputStream);
    7. List userList = reader.readAll(User.class);
    8. userService.saveBatch(userList);
    9. } catch (Exception e) {
    10. throw new ServiceException("文件上传失败");
    11. }
    12. return Result.success();
    13. }

    前端: 

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

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

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

  • 相关阅读:
    c 按位运算
    solidty-基础篇-结构体和数组,私有 / 公共函数,函数的返回值和修饰符,事件
    元数据管理平台Datahub0.10.5版本安装部署与导入各种元数据手册
    CentOS 7下yum安装GitLab CE
    web蓝桥杯真题:冰墩墩心情刻度尺
    设计模式 10 装饰器模式
    IP设置教程
    【算法题】210. 课程表 II
    7.9 用户接口
    ASP.NET Core 6框架揭秘实例演示[23]:ASP.NET Core应用承载方式的变迁
  • 原文地址:https://blog.csdn.net/abfwaaf/article/details/139659409