The complete front-end and back-end process of uploading a single file (with Token and other form information) based on SpringBoot + Vue

foreword

Sometimes when encountering such a requirement, you need to bring token credentials and other form information while uploading the file, so the front end of this requirement can be realized using the FormData data type. FormData is also passed through the body like JSON. The former supports strings and binary files, while the latter can only be strings, as shown in Figure 1 and Figure 2 below.

Figure 1

  Figure II

1. Backend code

(1) Control layer (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) Interface layer (IGameService.java)

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

(3) Implementation layer (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());
    }
}

2. Front-end code

(1) View page (/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(
        `/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>

3. Operation effect

Guess you like

Origin blog.csdn.net/Cai181191/article/details/131870024