前言
有时遇到这种需求,在上传文件的同时还需要带上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(
`/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>