本人业余前端开发,因为公司(很坑)觉得我很牛逼,所以让我前后端一起玩,无奈的我只能磕磕碰碰的研究起了vue。
开发项目的时候,用到文件上传的功能很常见,包括单文件上传和多文件上传,上传各种类型的文件。在vue里面要实现多文件上传功能,还是很方便的。
本文就一起来学习一下,如何把多文件上传功能封装成一个组件,后面需要使用的时候,直接两三行代码就能搞定。
1、前端代码
首先我们先看前端,如何把它封装成一个组件。我们在调用它的时候,可能需要从外部传入一些参数给它,所以我们需要定义一些传入参数。这些参数我们可以放到props里面
export default {
props: {
// 值
value: [String, Object, Array],
// 大小限制(MB)
fileSize: {
type: Number,
default: 2,
},
// 文件类型, 例如['png', 'jpg', 'jpeg']
fileType: {
type: Array,
default: () => [".jpg", ".jpeg", ".png", ".doc", ".xls", ".xlsx", ".ppt", ".txt", ".pdf"],
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: true
},
// 单据id
billId: {
type: Number,
require: true
},
// 单据类型
billType: {
type: Number,
required: true
}
}
}
上面定义了一些文件大小,文件类型等,如果没有传入,则为默认值。单据类型和单据id是必须要传入的,这里可以依照实际开发需要进行定义即可。
文件上传组件,我们可以使用element的el-upload
,在页面引入,我们点击后一般唤醒的是一个文件上传弹窗,可以使用el-dialog
标签包围。完整代码如下
选取文件
上传到服务器
请上传
大小不超过 {{ fileSize }}MB
格式为 {{ fileType.join("/") }}
的文件
删除
上面用到的几个变量,我们需要在data里面定义
data() {
return {
// 已选择文件列表
fileList: [],
// 是否显示文件上传弹窗
visible: false,
// 可上传的文件类型
accept: '',
action: 'action'
};
},
accept
字段需要在页面创建后,通过传入或默认的fileType
字段进行拼接得到
created() {
this.fileList = this.list
// 拼接
this.fileType.forEach(el => {
this.accept += el
this.accept += ','
})
this.fileType.slice(0, this.fileList.length - 2)
},
接下来就是文件上传相关的方法,这里我们使用选择文件后手动上传的方式,请看下面的代码
选取文件
上传到服务器
点击上传到服务器后,就触发submitUpload
调用我们自定义的方法。
注意这里选取文件的按钮需要加上slot="trigger"
属性,不然点击上传到服务器按钮后,也会触发选择文件弹框。
接下来看相关的方法
methods: {
// 外部调用这个方法,显示文件上传弹窗
show() {
this.visible = true
},
// 上传前校检格式和大小
handleBeforeUpload(file) {
// 校检文件类型
if (this.fileType) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf("."));
}
const isTypeOk = this.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
if (!isTypeOk) {
this.$message.error(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
return false;
}
}
// 校检文件大小
if (this.fileSize) {
const isLt = file.size / 1024 / 1024 < this.fileSize;
if (!isLt) {
this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
return false;
}
}
return true;
},
// 文件个数超出
handleExceed() {
this.$message.error(`只允许上传3个文件`);
},
// 上传失败
handleUploadError(err) {
this.$message.error("上传失败, 请重试");
},
// 上传成功回调
handleUploadSuccess(res, file) {
this.$message.success("上传成功");
this.cancel()
},
/** 文件状态改变时调用 */
handChange(file, fileList) {
this.fileList = fileList
},
// 删除文件
handleDelete(index) {
this.fileList.splice(index, 1);
},
// 获取文件名称
getFileName(name) {
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1).toLowerCase();
} else {
return "";
}
},
/** 手动提交上传 */
submitUpload() {
if (this.fileList.length <= 0) {
this.msgError('请选择文件上传')
return false
}
const formData = new FormData()
formData.append("billId", this.billId)
formData.append("formType", this.billType)
// 把多个文件放到同一个请求里,这样只会请求一次接口。否则一个文件就会请求一次
this.fileList.forEach(el => {
formData.append('file', el.raw)
})
uploadFormFile(formData).then(res => {
if(res.code === 200){
this.$refs.upload.clearFiles()
this.msgSuccess("上传成功")
this.$emit("input", res.data)
}
}).catch((err)=>{})
},
/** 关闭上传弹框 */
cancel() {
this.fileList = []
this.visible = false
},
}
其他方法都还好,主要看看这个submitUpload
方法,如果直接使用url的方式进行调用,会出现跨域问题,你需要把token放到请求头里。我这里是通过封装后的api接口进行调用的,已经做了全局的token验证,所以只需要把相关的参数带上即可。
使用formData
带上需要的参数。注意el-upload组件一次请求只会携带一个文件,所以即使是多文件上传,就会请求多次后端接口,这显然是不太好的。这里可以把多个文件一次性放到FormData里,这样就只会请求一次后端接口了。
// 把多个文件放到同一个请求里,这样只会请求一次接口。否则一个文件就会请求一次
this.fileList.forEach(el => {
formData.append('file', el.raw)
})
这就封装好了一个文件上传的组件,接下来看看怎么使用。
2、使用组件
在使用的页面,首先需要引入组件
import FileUpload from '@/components/FileUpload'
然后再页面内进行调用
billId
和billType
是我们动态传入的参数,其他的参数使用默认值,getAttachList
方法在文件上传成功后执行。
我们需要一个按钮唤醒文件上传框
上传
定义hangAdd
方法,直接调用组件里定义的show
方法即可
handleAdd() {
this.$refs.fileUploadDialog.show()
},
然后定义文件上传成功后的方法,获取已上传文件列表即可。
getAttachList() {
this.loading = true
this.attachQuery.billId = this.form.noticeId
this.attachQuery.billType = 10
listAttachment(this.attachQuery).then(response => {
this.attachList = response.rows
this.attachList.forEach(el => {
// 转为kb,取小数点后2位
el.fileSize = parseFloat(el.fileSize / 1024).toFixed(2)
})
this.attachTotal = response.total
this.loading = false
}).catch(() => {})
},
到这里前端代码就大功告成了。
3、后端代码
我这里选择上传图片到阿里云服务器,上传到哪里不重要
/**
* 单据附件上传
* @param billId 单据id
* @param formType 单据类型
* @param file 上传的文件
* @return
* @throws Exception
*/
@PostMapping("/form/attachment/upload")
public AjaxResult uploadFormFile(@RequestParam(value = "billId") Long billId,
@RequestParam(value = "formType") Integer formType,
@RequestParam("file") MultipartFile[] file) throws Exception {
try {
// 文件上传后的路径
List pathList = new ArrayList<>();
for (MultipartFile f : file) {
if (!f.isEmpty()) {
// 调用接口上传照片
AjaxResult result = uploadService.uploadFormFile(f, billId, formType);
if (!result.get("code").toString().equals("200")) {
return AjaxResult.error("上传文件异常,请联系管理员");
}
pathList.add(result.get("data").toString());
}
}
// 返回图片路径
return AjaxResult.success(pathList);
} catch (Exception e) {
return AjaxResult.error(e.getMessage());
}
}
这里我们使用一个MultipartFile
数组接受文件,然后调用方法判断文件的一些属性上传文件即可,具体的上传方法这里就不贴了,各有不同,具体情况具体分析。
到这里,本文就ok了。关于MIME 类型列表,可以点击这里查看。如果过程中遇到什么问题,可以在下方留言,我会回复的。