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>