springboot+vue2+elementui+mybatis- 批量导出导入

全部导出

批量导出

报错问题分析

经过排查,原因是因为在发起 axios 请求的时候,没有指定响应的数据类型(这里需要指定响应的数据类型为 blob 二进制文件)

当响应数据回来后,会执行 axios 后置拦截器的代码,因为没有对响应头的类型进行判断,而是判断为字符串 String,将该流转为 JSON 对象而报错

导出的 思路分析:

  1. 得到用户选中的 ids 数组(前端 vue)

  2. 请求导出的后台接口

  3. 根据 id,从数据库中查询记录(springboot)

4.拿到数据之后,使用流的方式,响应给浏览器/客户端

Vue2+Elementui

1.新增导出按钮

复制代码
<el-button type="primary" @click="exportUsers">批量导出</el-button>

2.将选择的 ids 集合当作参数提交给后端 springboot

复制代码
    //导出
    exportUsers() {//如果没有选择行数据,则全部导出或者按照检索条件导出

      this.$confirm("您是否需要导出?", "提示", {
        iconClass: "el-icon-question",//自定义图标样式
        confirmButtonText: "确认",//确认按钮文字更换
        cancelButtonText: "取消",//取消按钮文字更换
        showClose: true,//是否显示右上角关闭按钮
        type: "warning",//提示类型  success/info/warning/error
      }).then(() => {
        //确认操作
        //请求批量导出的接口
        this.$request.get('/user/exportUsersById', {
          params: { //ids携带过去,
            ids: this.ids //存的是勾选的id的数组
          },
          responseType: 'blob', // 设置响应类型为blob(响应的数据是二进制文件)
          paramsSerializer: params => {//get方法,传的参数的是数组解决uri的路径问题 ?ids[]=225&ids[]=226
            return qs.stringify(params, {indices: false})
          }
        }).then(response => {
          // console.log("response=", response)
          if (response.size > 0) {//返回的是blob,判断文件的大小

            this.$message.success("导出成功");
          } else {
            this.$message.warning("导出失败");
          }
        })
      }).catch(() => {
        //取消操作
      });
    },

3.在 axios 后置拦截器中将 blob 二进制文件转为 excel

复制代码
import axios from 'axios'
import router from "@/router";
import {saveAs} from 'file-saver';//导入该依赖

// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(response => {
    //简化.data操作 直接使用res.data就能得到数据
    let res = response.data;
    console.log("res=", res)


    // 判断是否为二进制数据
    if (response.config.responseType === 'blob') {
        console.log("该文件是二进制文件")
        // 从响应头中获取文件名
        const contentDisposition = response.headers.get('Content-Disposition');
        const filenameRegex = /filename=(.+)/
        const fileNameMatch = contentDisposition && contentDisposition.match(filenameRegex);
        const fileName = fileNameMatch && fileNameMatch[1];
        // 对文件名进行解码,换原成原始文件名
        const decodedFileName = decodeURIComponent(fileName);
        // 对文件名进行解码,换原成原始文件名
        // const decodedFileName = decodeURIComponent(fileName);
        // 使用FileSaver库保存文件
        const blob = new Blob([response.data], {type: response.headers['content-type']});
        // saveAs(blob, 'file.xlsx');
        saveAs(blob, decodedFileName || 'file.xlsx');
    }
    // // 兼容服务端返回的字符串数据
    //  if (typeof res === 'string') {
    //      res = res ? JSON.parse(res) : res
    //  }
    // //返回接口的状态码401,返回登录页面
    // if (res.code === '401') {
    //     router.push('/login')
    // }
    return res;
}, error => {
    console.error('response error: ' + error) // for debug
    return Promise.reject(error)
})

SpringBoot

导入 maven 依赖

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

@RequestMapping("/exportUsersById")
public void exportUsersById(@RequestParam(value = "ids", required = false) List<Integer> ids, HttpServletResponse response) {
    log.info("idList=" + ids);
    List<User> userList;// 用户数据集合
    String fileName = "";// 文件名

    if (ObjectUtil.isEmpty(ids) || ids.contains(-1)) {// 全部导出
        userList = UserServiceImpls.list();
        fileName = "所有用户";
        log.info("所有用户=" + userList);
        if (ObjectUtil.isEmpty(userList)) { // 列表为空
            throw new BizException("用户列表为空");
        }
    } else {
        userList = UserServiceImpls.listByIds(ids);// 批量导出
        fileName = "部分用户";
        log.info("部分用户=" + userList);
    }

    // 使用huTools工具类-Excel导出
    ExcelWriter writer = ExcelUtil.getWriter(true);
    writer.write(userList, true);

    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
    try {
        // 将Content-Disposition暴露,前端才能得到Content-Disposition的value值
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8") + ".xlsx");
        writer.flush(response.getOutputStream(), true);
    } catch (IOException e) {
        // e.printStackTrace();
    } finally {
        writer.close();
    }
}

批量导入

复制代码
<el-upload
  style="display: inline-block"
  action="http://localhost:9000/user/importData"
  :headers="{token:loginUSer.token}"
  :show-file-list="false"
  :on-success="handleFileSuccess">
  <el-button type="success" class="my-button">批量导入</el-button>
</el-upload>

/**
     * 导入数据
     *
     * @param file:
     * @return ResultResponse<String>
     * @author "卒迹"
     * @description TODO
     * @date 17:32
     */

@PostMapping("/importData")
public ResultResponse<String> importData(MultipartFile file) throws IOException {
    // 写入文件流
    ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
    // 以User类的格式导入数据-返回1个集合对象,这里取决于alias注解
    List<User> userList = reader.readAll(User.class);
    if (ObjectUtil.isEmpty(userList)) {// 导入的数据为空
        return ResultResponse.error("导入失败");
    }
    // 写入数据到数据库
    boolean isSave = false;
    try {
        isSave = UserServiceImpls.saveBatch(userList);
    } catch (Exception e) {
        // e.printStackTrace();
        return ResultResponse.error("导入出错");
    }
    return isSave ? ResultResponse.success("导入成功") : ResultResponse.error("导入失败");
}

//导入
handleFileSuccess(response, file, fileList) {
  console.log("response=", response)
  if (response.code === "2000") {
    this.$message.success(response.message);
    //刷新数据
    this.queryUserByUsernameOrName(this.isDisplayMsg = false, 1)
  }
  if (response.code === "-1") {
    this.$message.error(response.message);
  }

},
相关推荐
用户908324602736 小时前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
用户8307196840821 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解1 天前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解1 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记1 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者2 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840822 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解2 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者3 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq
花花无缺3 天前
搞懂@Autowired 与@Resuorce
java·spring boot·后端