基于SpringBoot + Vue实现单个文件上传(带上Token和其它表单信息)的前后端完整过程

news2024/11/25 13:53:27

有时遇到这种需求,在上传文件的同时还需要带上token凭据和其它表单信息,那么这个需求前端可以使用FormData数据类型来实现。FormData和JSON一样也是通过body传递的,前者支持字符串和二进制文件,后者只能是字符串,如下图1,图2所示。

图一

  图二

一、后端代码

(1)控制层(GameController.java)

@PostMapping(value = "uploadSingleFile")
@ResponseBody
public CommonResponse uploadSingleFile(@RequestHeader("Authorization") String token,
                                       @RequestParam("file") MultipartFile file,
                                       @RequestParam("username") String username) {
    return gameService.uploadSingleFile(token, file, username);
}

(2)接口层(IGameService.java)

CommonResponse uploadSingleFile(String token, MultipartFile file, String username);

(3)实现层(GameServiceImpl.java)

@Value("${system.upload-file-path}")
private String UploadFilePath;

@Override
public CommonResponse uploadSingleFile(String token, MultipartFile file, String username) {
    System.out.println(token);
    System.out.println(username);
    try {
        String uuidStr = UUID.randomUUID().toString();
        String uuid = uuidStr.substring(0, 8) + uuidStr.substring(9, 13) + uuidStr.substring(14, 18) + uuidStr.substring(19, 23) + uuidStr.substring(24);

        String originFileName = file.getOriginalFilename(); // 原文件名,如:HelloWorld.xlsx
        int beginIndex = originFileName.lastIndexOf("."); // 从后匹配"."
        String newFileName = uuid + originFileName.substring(beginIndex); // 新文件名,如:uuid.xlsx
        String destFileName = UploadFilePath + File.separator + newFileName; // 完整文件名 = 存储路径 + 原文件名

        // 复制文件到指定目录
        File destFile = new File(destFileName);
        destFile.getParentFile().mkdirs();
        file.transferTo(destFile);

        // 返回文件名
        return CommonResponse.ok(newFileName);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        return CommonResponse.fail(e.getMessage());
    } catch (IOException e) {
        e.printStackTrace();
        return CommonResponse.fail(e.getMessage());
    }
}

二、前端代码

(1)视图页面(/src/view/Example/UploadFormData/index.vue)

<template>
  <div>
    <el-dialog
      width="400"
      title="导入 Excel"
      class="import-excel-dialog"
      align-center
      destroy-on-close
      draggable
      center
      v-model="importExcelDialog.isVisible"
      :before-close="handleCloseImportExcelDialogClick"
    >
      <div>
        <p style="margin: 0 auto 7px 0; font-size: 13px">请上传Excel文件</p>
        <el-upload
          ref="importExcelUploadRef"
          drag
          action=""
          :limit="1"
          :on-exceed="handleUploadFileExceed"
          :on-change="handleUploadFileChange"
          :auto-upload="false"
        >
          <el-icon class="el-icon--upload"><upload-filled /></el-icon>
          <div class="el-upload__text" style="font-size: 13px">
            拖动文件到此处进行上传 或 <em>点击上传</em>
          </div>
          <template #tip>
            <div class="el-upload__tip">支持格式 : xls/xlsx/xlsm</div>
          </template>
        </el-upload>
 
        <el-input
          size="small"
          v-model="importExcelDialog.username"
          style="width: 100%; background-color: #eee"
          placeholder="请输入用户"        
        >
        </el-input>
      </div>
 
      <template #footer>
        <div>
          <el-button size="small" type="primary" @click="handleUploadFileComfirm($event)">
            <el-icon :size="18" style="margin-right: 5px"><Check /></el-icon>
            <small>确定</small>
          </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
 
<script>
export default {
  data: () => {
    return {
      // 导入Excel弹窗
      importExcelDialog: {
        isVisible: false,
        username: '帅龍之龍',
        uploadFileList: [], // 校验通过的上传文件列表
      },
    }
  },
  created() {
    // ...
  },
  mounted() {
    this.handleOpenImportExcelDialogClick()
  },
  methods: {
    /**
     * 打开导入Excel弹窗事件句柄
     */
    handleOpenImportExcelDialogClick() {
      this.importExcelDialog.isVisible = true
    },
 
    /**
     * 文件上传 - 文件超过句柄方法
     */
    async handleUploadFileExceed(files, uploadFiles) {
      console.log(
        '%c 文件超过句柄方法 %c handleUploadFileExceed',
        'padding: 1px; background-color: #35495e; color: #fff',
        'padding: 1px; background-color: #5e7ce0; color: #fff',
      )
      console.log('%c ∟ %c files %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', files)
      console.log('%c ∟ %c uploadFiles %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', uploadFiles)
 
      const refs = await this.$refs
      const importExcelUploadRef = refs.importExcelUploadRef
 
      const uploadFile = files[0]
      const fileName = uploadFile.name // xxxxxx.xlsx
      const index = fileName.lastIndexOf('.')
      const type = fileName.substring(index) // .xlsx
 
      if (!(type == '.xls' || type == '.xlsx' || type == '.xlsm')) {
        this.$message({ message: '文件格式错误,应为 xls/xlsx/xlsm 类型文件', type: 'warning', duration: 3000 })
 
        // 清空
        importExcelUploadRef.clearFiles()
      } else {
        // 清空
        importExcelUploadRef.clearFiles()
 
        // 回显
        const file = files[0]
        importExcelUploadRef.handleStart(file) 
 
        // 文件上传列表重新赋值
        this.importExcelDialog.uploadFileList = []
        this.importExcelDialog.uploadFileList.push(uploadFile)
      }
    },
 
    /**
     * 文件上传 - 文件改变句柄方法
     */
    async handleUploadFileChange(uploadFile, uploadFiles) {
      console.log(
        '%c 文件改变句柄方法 %c handleUploadFileChange',
        'padding: 1px; background-color: #35495e; color: #fff',
        'padding: 1px; background-color: #5e7ce0; color: #fff',
      )
      console.log('%c ∟ %c uploadFile %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', uploadFile)
      console.log('%c ∟ %c uploadFiles %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', uploadFiles)
 
      const refs = await this.$refs
      const importExcelUploadRef = refs.importExcelUploadRef
      
      const fileName = uploadFile.name // xxxxxx.xlsx
      const index = fileName.lastIndexOf('.')
      const type = fileName.substring(index) // .xlsx
 
      if (!(type == '.xls' || type == '.xlsx' || type == '.xlsm')) {
        this.$message({ message: '文件格式错误,应为 xls/xlsx/xlsm 类型文件', type: 'warning', duration: 3000 })

        // 清空
        importExcelUploadRef.clearFiles()
      } else {
        // 文件上传列表重新赋值
        this.importExcelDialog.uploadFileList = []
        this.importExcelDialog.uploadFileList.push(uploadFile)
      }
      console.log('%c ∟ %c uploadFileList %c =>', 'text-indent: 10px', 'padding: 1px; background-color: #41b883; color: #fff', 'color: #000', this.importExcelDialog.uploadFileList)
    },
 
    /**
     * 文件上传 - 确认上传句柄方法
     */
    async handleUploadFileComfirm(evt) {
      this.$elementUtil.handleElButtonBlur(evt)
 
      if (this.importExcelDialog.uploadFileList.length == 0) {
        this.$message({ message: '请上传文件', type: 'warning', duration: 3000 })
        return
      }

      let formData = new FormData()
      this.importExcelDialog.uploadFileList.map(uploadFile => {
        formData.append('file', uploadFile.raw)
      })
      formData.append('username', this.importExcelDialog.username.trim())

      this.$axios.post(
        `/sz-senwill-sky/api/uploadSingleFile`,
        formData,
        {
          headers: {
            'Access-Control-Allow-Origin': '*',
            'Content-Type': 'multipart/form-data',
            'Authorization': 'Bearer token'
          },
        }
      )
      .then((res) => {
        console.log('get =>', res)
        if (res.status == 200 && res.data) {
          const result = res.data
          if (result.code == 200 && result.success) {
            this.$elementUtil.handleAutoCloseMessage(`上传成功,新文件名为:${result.data}`, 'success', 5, true)
          } else {
            this.$elementUtil.handleAutoCloseMessage(`上传失败,服务端出错,请联系管理员`, 'error', 5, true)
          }
        }

        setTimeout(() => { this.importExcelDialog.isVisible = false }, 1000)
      }).catch(err =>{
        this.$elementUtil.handleAutoCloseMessage(err, 'error', 5, true)
        console.error(err)
      })

      const refs = await this.$refs
      const importExcelUploadRef = refs.importExcelUploadRef

      // 清空
      importExcelUploadRef.clearFiles()

      // 文件上传列表重置
      this.importExcelDialog.uploadFileList = []
    },
 
    /**
     * 关闭导入Excel弹窗事件句柄
     */
    handleCloseImportExcelDialogClick() {
      this.importExcelDialog.isVisible = false
    },
  }
}
</script>
 
<style lang="less" scoped>
  :deep(.import-excel-dialog) {
 
    .el-dialog__header {
      margin-right: 0;
      padding: 25px 20px;
      border-bottom: 1px solid #efefef;
 
      .el-dialog__title {
        font-size: 15px;
        word-wrap: break-word;
        word-break: normal
      }
 
      .el-dialog__headerbtn {
        top: 15px;
        font-size: 20px;
      }
    }
 
    .el-dialog__body {
      padding: calc(var(--el-dialog-padding-primary) + 10px) var(--el-dialog-padding-primary);
 
      .el-select {
 
        .el-input {
        
          .el-input__wrapper {
            background-color: transparent;
          }
        }
      }
 
      .el-input {
        
        .el-input__wrapper {
          background-color: #eee;
        }
      }
    }
 
    .el-dialog__footer {
      padding: var(--el-dialog-padding-primary);
      border-top: 1px solid #efefef;
    }
  }
</style>

三、效果如下 ~

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

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

相关文章

[CISCN 2023 初赛]go_session 解题思路过程

过程 下载题目的附件&#xff0c;是用go的gin框架写的后端&#xff0c;cookie-session是由gorilla/sessions来实现&#xff0c;而sessions库使用了另一个库&#xff1a;gorilla/securecookie来实现对cookie的安全传输。这里所谓的安全传输&#xff0c;是指保证cookie中的值不能…

STM32 USB使用记录:HID类设备(前篇)

文章目录 目的基础说明HID类演示代码分析总结 目的 USB是目前最流行的接口&#xff0c;现在很多个人用的电子设备也都是USB设备。目前大多数单片机都有USB接口&#xff0c;使用USB接口作为HID类设备来使用是非常常用的&#xff0c;比如USB鼠标、键盘都是这一类。这篇文章将简单…

向npm注册中心发布包(上)

目录 1、创建package.json文件 1.1 fields 字段 1.2 Author 字段 1.3 创建 package.json 文件 1.4 自定义 package.json 的问题 1.5 从当前目录提取的默认值 1.6 通过init命令设置配置选项 2、创建Node.js 模块 2.1 创建一个package.json 文件 2.2 创建在另一个应用程…

UE5 AI移动无动作问题

文章目录 问题背景解决方法问题背景 在使用行为树让角色移动时,出现角色行走不播放动作的情况: 解决方法 其实有2个问题导致出现这种情况 1、角色动画蓝图的问题 角色动画蓝图可能存在4个问题: ① 无播放行走动画 ② 速度的值未正常传递 ③ 播放移动动作逻辑的值判断错…

【每日一题】——C - Standings(AtCoder Beginner Contest 308 )

&#x1f30f;博客主页&#xff1a;PH_modest的博客主页 &#x1f6a9;当前专栏&#xff1a;每日一题 &#x1f48c;其他专栏&#xff1a; &#x1f534; 每日反刍 &#x1f7e1; C跬步积累 &#x1f7e2; C语言跬步积累 &#x1f308;座右铭&#xff1a;广积粮&#xff0c;缓称…

Clion开发STM32之W5500系列(DNS服务封装)

概述 在w5500基础库中进行封装&#xff0c;通过域名的方式获取实际的ip地址用于动态获取ntp的ip地址 DNS封装 头文件 /*******************************************************************************Copyright (c) [scl]。保留所有权利。****************************…

JVM对象在堆内存中是否如何分配?

1&#xff1a;指针碰撞&#xff1a;内存规整的情况下 2&#xff1a;空闲列表: 内存不规整的情况下 选择那种分配方式 是有 java堆是否规整而决定的。而java堆是否规整是否对应的垃圾回收器是否带有空间压缩整理的能力决定的。 因此当使用Serial,ParNew等带有压缩整理过程的收…

win10环境下,应用无法启动并被删除(无法完成操作,因为文件包含病毒或潜在的垃圾文件)

现象&#xff1a; 解决办法&#xff1a; 一、关闭所有自己安装的杀毒软件&#xff0c;如&#xff1a;360安全卫士&#xff0c;金山毒霸等 二、关闭win10本身的杀毒软件&#xff0c;步骤如下&#xff1a; 1、搜索栏输入“病毒和威胁防护” 2、进入以后&#xff0c;点击"病…

Pytorch迁移学习使用Resnet50进行模型训练预测猫狗二分类

目录 1.ResNet残差网络 1.1 ResNet定义 1.2 ResNet 几种网络配置 1.3 ResNet50网络结构 1.3.1 前几层卷积和池化 1.3.2 残差块&#xff1a;构建深度残差网络 1.3.3 ResNet主体&#xff1a;堆叠多个残差块 1.4 迁移学习猫狗二分类实战 1.4.1 迁移学习 1.4.2 模型训练 1.…

vue3基础+进阶(二、vue3常用组合式api基本使用)

目录 第二章、组合式API 2.1 入口&#xff1a;setup 2.1.1 setup选项的写法和执行时机 2.1.2 setup中写代码的特点 2.1.3 script setup语法糖 2.1.4 setup中this的指向 2.2 生成响应式数据&#xff1a;reactive和ref函数 2.2.1 reactive函数 2.2.2 ref函数 2.2.3 rea…

Cesium态势标绘专题-入口

本专题没有废话,只有代码,撸! 标绘主类MilitaryPlotting.ts /** 态势标绘主类* @Author: Wang jianLei* @Date: 2023-01-13 14:47:20* @Last Modified by: jianlei wang* @Last Modified time: 2023-05-31 09:55:34*/ import * as Creator from ./create/index; import Cre…

S32K324双核的核间通信使用示例

文章目录 前言修改ld文件核0的ld文件核1的ld文件 定义共享数据使用共享数据编译共享数据文件总结 前言 最近项目用S32K324开发&#xff0c;暂时只用了MCAL&#xff0c;没有Autosar上层的模块&#xff0c;最开始用官方给的demo工程双核可以正常跑起来&#xff0c;但实际开发时都…

使用nginx和ffmpeg搭建HTTP FLV流媒体服务器(摄像头RTSP视频流->RTMP->http-flv)

名词解释 RTSP &#xff08;Real-Time Streaming Protocol&#xff09; 是一种网络协议&#xff0c;用于控制实时流媒体的传输。它是一种应用层协议&#xff0c;通常用于在客户端和流媒体服务器之间建立和控制媒体流的传输。RTSP允许客户端向服务器发送请求&#xff0c;如…

数据分析工具与技术

数据分析工具与技术 数据分析技术 数据分析工具 备选方案分析 一种对已识别的可选方案进行评估的技术&#xff0c;用来决定选择哪种方案 或使用何种方法来执行项目工作。 其他风险参数评估 为了方便未来分析和行动&#xff0c;在对单个项目风险进行优先级排序时&#xff0…

GO内存模型(同步机制)

文章目录 概念1. 先行发生 编译器重排同步机制init函数协程的创建channelsync 包1. sync.mutex2. sync.rwmutex3. sync.once atomic 参考文献 概念 1. 先行发生 The happens before relation is defined as the transitive closure of the union of the sequenced before and …

超详细-Vivado配置Sublime+Sublime实现Verilog语法实时检查

目录 一、前言 二、准备工作 三、Vivado配置Sublime 3.1 Vivado配置Sublime 3.2 环境变量添加 3.3 环境变量验证 3.4 Vivado设置 3.5 配置验证 3.6 解决Vivado配置失败问题 四、Sublime配置 4.1 Sublime安装Package Control 4.2 Sublime安装Verilog插件 4.3 安装语…

centos7中MySQL备份还原策略

目录 一、直接拷贝数据库文件 1.1在shangke主机上停止服务并且打包压缩数据库文件 1.2 在shangke主机上把数据库文件传输到localhost主机上(ip为192.168.33.157) 1.3在localhost主机上停止服务&#xff0c;解压数据库文件 1.4 在localhost主机上开启服务 1.5 测试 二、m…

利用@Excel实现复杂表头导入

EasyPoi导入 <a-upload name"file" :showUploadList"false" :multiple"false" :headers"tokenHeader" :action"importExcelUrl"change"handleImportExcel"><a-button type"primary" icon&quo…

【软件测试】如何选择回归用例

目录 如何在原始用例集中挑选测试用例 具体实践 总结 本文讨论一下在回归测试活动中&#xff0c;如何选择测试用例集。 回归测试用例集包括基本测试用例集&#xff08;原始用例&#xff09;迭代新增测试用例集&#xff08;修复故障引入的用例和新增功能引入的用例集&#xf…

洛必达法则和分部积分的应用之计算数学期望EX--概率论浙大版填坑记

如下图所示&#xff0c;概率论与数理统计浙大第四版有如下例题&#xff1a; 简单说就是&#xff1a;已知两个相互独立工作电子装置寿命的概率密度函数&#xff0c;将二者串联成整机&#xff0c;求整机寿命的数学期望。 这个题目解答中的微积分部分可谓是相当的坑爹&#xff0c;…