在前端开发的时候,特别是现在使用框架开发,我们前端必须要掌握的一个功能就是文件上传,不管是单文件,多文件,在平时需求当中肯定是会遇到的,虽然一些组件库已经实现了大部分文件上传的封装,但是,有时候需求往往更多,需要我们根据需求定制。本文主要分享的是我在前几天做后台管理系统需要实现的一个文件上传功能,需求包含:上传文件队列的显示,进度的展示,以及可以批量取消上传的功能,当时我是看了下组件库,貌似组件库都没有现成的功能,因此只有自己实现这个功能了,说复杂也不太复杂,说简单也不太简单,当时我自己因为没做过类似功能,因此结合查询资料,整整弄了一下午,最后才做出来,现在我分享我个人思路和代码,如果大家有类似需求,没有头绪的可以参考我这个。
效果图
在正式的开始前,分享几张功能的截图好了,大家可以参考参考,功能不华丽,勿喷。 这里我就直接上传两个视频好了,普通的表格图片都太小了,看不出来具体列表效果。
- 页面上传按钮
- 支持多选,以及文件类型,可以自己限制
- 上传当中的文件队列,上传完毕的文件,会从文件队列删除
- 取消所有的上传
主要知道的几个点
组件库我使用的iview里面的upload组件,组件库大同小异,看个人需求iview,组件库地址贴上了,感兴趣的可以看看。我们公司后台使用的这个,相比element,这个组件库的样式看着还是更加华丽写的(个人观点)
- upload组件用法(不使用组件库,原生input,file也可以,样式得自己调) -axios -axios里面canceltken(用户取消请求) -modal弹窗组件 -axios中的onUploadProgress配置项(做上传进度的关键)
代码部分
- template模板
//文件上传按钮
<Upload
:action="BASEURL + '/RedeemCode/ImportRedeemCode'"//你的文件上传地址
:data="uploadParams"//后端要求的附加参数
accept=".xlsx,.xls,.xlsm,.png,.webp,.mp4"//接收的文件类型
:on-format-error="handleFormatError"//文件格式不匹配的回调
:on-success="handleSuccess"//上传成功的回调
:before-upload="handleUpload"//上传之前(我们要实现自己的功能,这个回调用于阻止默认自动上传)
:show-upload-list="false"//不显示文件上传队列(因为我们自己自定义文件队列,默认的也不好用我们舍弃)
ref="upload"//这个我不说了,用于获取组件实例的
multiple//支持多选(看你需求)
>
<Button icon="ios-cloud-upload-outline">导入兑换码</Button>//文件上传的插槽内容,看自己随便改
</Upload>
//文件上传队列
<!-- S 进度弹窗 -->
<Modal//使用弹窗,上传完毕就自动关闭,上传就自动打开
v-model="processModal"
title="上传文件队列"
footer-hide
:mask-closable="false"
:before-close="processModalClose"//关闭前的回调
>
<template v-for="(item, index) in filesList">//这里就是显示上传队列的,上传完毕就不显示了,根据进度判断
<div :key="index" v-if="item.percent < 99 && item.percent > 0">
<p>{{ item.name }}</p>
<Progress :percent="item.percent" status="active" />
</div>
</template>
</Modal>
<!-- E 进度弹窗 -->
- js
//data部分
data(){
return {
uploadParams: {
//文件上传功能参数(我这接口需要的,你那自己定义)
X_Public_AccessToken: this.$cookies.get("x_public_AccessToken") || "",
X_Public_DeviceToken: this.$cookies.get("deviceToken") || "",
random: Math.random(),
X_Public_Nonce: this.$uuid(),
X_Public_TimeStamp: this.$getUtcTime(new Date()),
X_Public_AppId: APPID,
p: "!@$12345",
userid: this.$cookies.get("userId"),
},
processModal:false,控制弹窗显示
filesList:[],上传当中的文件队列
uploadCount:0,处于上传中的文件数量,当等于fileList的时候就关闭弹窗,请求完毕一个就++
cancelUploadArr:[],保存每个文件上传接口对应的取消请求的函数[fn,fn,fn...],点叉叉关闭弹窗就遍历这个全部执行一遍就取消了所以请求,(有那么点点发布订阅的思想,关闭弹窗,通知取消请求,我这里是全部取消,也可以根据需要,取消某一个请求,思想都一样的)
}
}
//axios上传接口的封装
// 做取消请求预备
let CancelToken = Axios.CancelToken;//构造函数(保存下来)这个用法看官方文档
const importReedeemCode = (params,callback,cancelCallBack) => {
//导入兑换码
return axios.post("/RedeemCode/ImportRedeemCode", params, {
onUploadProgress(progressEvent) {//详情见axios官网(该配置项用户动态获取进度)===》上传进度需求很好用
if (progressEvent.lengthComputable) {
callback(progressEvent);
}
},
cancelToken: new CancelToken(function executor(c) {
cancelCallBack(c)// 这个参数 c 就是CancelToken构造函数里面自带的取消请求的函数,这里把该函数当参数用,我们吧这个c保存到cancelUploadArr当中后期
})
});
};
//methods部分
// 上传成功
handleSuccess(res) {
res.Success && this.$Message.success("上传成功");//提示
this.processModal = false;//成功后关闭弹窗
this.filesList = [];
},
// 手动控制上传
handleUpload(file) {
this.processModal = true;//显示弹窗
this.filesList.push({ file, name: file.name });//文件和文件名加到上传进度,上面渲染要展示和下面调用要使用
return false;//组织自动上传
},
// 文件上传
uploadSelf(file, index) {//关键,watch里面调用,参数为文件和在filelist中的索引位置
const params = {//上传额外的参数
p: "!@$12345",
userid: this.$cookies.get("userId"),
};
params.file = file;
let fileForm = new FormData();//文件上传记得模拟form表单,不然接口参数回丢失
for (let key in params) {
fileForm.append(key, params[key]);
}
/* api调用方法我挂载到了Vue原型上
参数1:文件上传参数
参数2:文件上传进度回调,这里用到了函数颗粒化,类似react中的事件绑定,具体为啥这里,可以看我前面的专题,
参数2:文件上传取消回调
*/
this.$Apis.importReedeemCode(fileForm, (progressEvent) =>this.uploadUnderWayCallback(progressEvent, index),(c)=>this.cancelCallBack(c,index))
.then((res) => {
console.log(this.filesList,this.uploadCount)
this.uploadCount++;//成功就++
if (this.uploadCount == this.filesList.length) {//代表列表全部上传完毕
this.processModal = false;
}
});
},
// 文件上传进度回调
uploadUnderWayCallback(progressEvent, index) {
let completeVal = (progressEvent.loaded / progressEvent.total) * 100 || 0;//处理成组件库进度组件需要的格式
this.$set(this.filesList[index], "percent", Math.floor(completeVal));//直接改变数组某一项不是响应式的,因此我们需要用到这个api,将其响应式化,添加到数组中
},
// 文件取消回调
cancelCallBack(c,index){
this.$set(this.cancelUploadArr, index, c);//添加取消队列中,关闭弹窗批量取消
this.uploadCount=0;
},
// 图片格式不正确
handleFormatError() {
this.$Message.error("文件格式为xlsx或xls");
},
// 进度弹窗关闭;记得取消所有列表请求
processModalClose() {
// 取消请求
if (this.uploadCount != this.filesList.length) {//说明都上传完毕了就没必要取消了
this.cancelUploadArr.forEach(cancelCallBack=>cancelCallBack());//批量取消上传
this.$Message.warning("取消上传!");
}
this.processModal = false;//最后关闭
},
//watch部分
watch: {//监听弹窗变化
processModal(val) {//弹窗关闭记得清空,开启执行批量上传
if (val) {
for (let i = 0; i < this.filesList.length; i++) {
this.uploadSelf(this.filesList[i].file, i);//弹窗显示的时候,就根据文件队列的数量调用上传接口
}
} else {//弹窗关闭,记得还原数据到初始位置
this.filesList = [];
this.uploadCount = 0;
this.cancelUploadArr=[];
console.log(this.filesList,this.uploadCount)
}
},
},
小结
文件上传是前端开发中必须要掌握的点,需求多样化,但是本质还是一样的,上面需要掌握的知识点有:
- 函数颗粒化,高阶函数
- 组件库的使用
- 发布订阅模式的了解
- axios的使用(本文关键点使用到了axios的取消请求和进度控制)===》参考官网文档可以详细看下配置
- v-for和v-if的嵌套(这也是我之前面试遇到的一个问题,实际开发中叶遇到不少,需要熟练掌握)
- 一切新知识,建议大家先看文档,不懂,再百度,文档永远是最官方的。
最后,大家喜欢,记得收藏订阅,谢谢呀!