El proceso completo de front-end y back-end de carga de un solo archivo (con Token y otra información de formulario) basado en SpringBoot + Vue

prefacio

A veces, cuando se encuentra con un requisito de este tipo, debe traer credenciales de token y otra información del formulario mientras carga el archivo, por lo que la parte frontal de este requisito se puede realizar utilizando el tipo de datos FormData. FormData también se pasa a través del cuerpo como JSON. El primero admite cadenas y archivos binarios, mientras que el segundo solo puede ser cadenas, como se muestra en la Figura 1 y la Figura 2 a continuación.

Figura 1

  Figura II

1. Código de fondo

(1) Capa de control (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) Capa de interfaz (IGameService.java)

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

(3) Capa de implementación (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. Código de front-end

(1) Ver página (/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. Efecto de operación

Supongo que te gusta

Origin blog.csdn.net/Cai181191/article/details/131870024
Recomendado
Clasificación