基于Element UI,封装一个头像上传组件。上传组件被抽离为一个单独组件(子组件),确认和取消按钮、用户头像则在父组件中

核心思路
- 后端方面,采用腾讯云COS代替。
- 文件上传到腾讯云后,拿到腾讯云响应的头像在线地址,将该地址设定为头像img标签的src属性值,实现头像的预览。
- 当你点击“保存更新”后,通过发ajax请求,将头像在线地址发送到我们的服务器中保存。
- 任何需要头像的地方,只需要再发请求,就能获取在线头像。或者,将在线地址存到vuex中,实现共享
第一步:改造upload组件
核心代码如下:
<el-upload
class="avatar-uploader"
action="#"
:http-request="upload"
:show-file-list="false"
:before-upload="beforeAvatarUpload"
>
<!-- 使用插槽 -->
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon" />
<div slot="tip" class="el-upload__tip">
注意:上传的头像图片只能是JPG格式! 大小不能超过2MB !
</div>
<!-- el-progress 进度条 -->
<el-progress v-if="showProgress" type="circle" :percentage="percent" class="progress"/>
</el-upload>
解释如下:
http-request属性:
用于自定义的上传行为, 属性值是函数。使用该属性会覆盖组件默认的上传行为,一旦使用之后,所有的上传操作都需要自己去实现。所以这些属性会失效:on-success属性(属性值是文件上传成功时的钩子函数)、action等,但文件上传之前的属性不会失效,例如“ before-upload属性 ”, 它的属性值是上传文件之前的钩子函数
upload函数:它是http-request的属性值,函数的参数是事件对象e,事件对象e有很对属性, 包括File文件对象属性
action属性:必选参数,上传的地址。即便使用http-request属性自定义上传行为也不能省略该action参数。由于我们需要定制上传行为,所以这里设为 #
show-file-list属性:是否显示已上传文件列表
before-upload属性:属性值是上传文件之前的钩子函数beforeAvatarUpload,函数的形式参数file是原生的File对象(文件对象),如果函数的返回值为false,则停止上传。使用该属性能在上传前,对图片的大小、格式等校验进行校验。file是文件对象,有这些属性:文件名name、文件大小size、文件类型type等
el-progress标签:环形进度条,显示上传进度
第二步:校验头像文件
核心代码如下:
methods: {
// 上传前的校验
beforeAvatarUpload(file) {
// console.log(file)
// 文件类型判断
const isJPG = file.type === "image/jpeg";
// 文件大小判断
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error("上传头像图片只能是 JPG 格式!");
}
if (!isLt2M) {
this.$message.error("上传头像图片大小不能超过 2MB!");
}
// 两个条件中,如果有一个不符合,则停止上传
return isJPG && isLt2M;
}
第三步:自定义上传行为与使用腾讯云COS
- 文件上传的操作都在
upload函数中进行 - 在腾讯云控制台一顿配置后,再安装腾讯云COS的SDK依赖:
npm i cos-js-sdk-v5 --save - onProgress 表示上传的进度信息,可配合el-progress在页面显示上传进度。在上传时,cos支持配置onProgress回调,在这个回调函数中可以拿到当前上传的进度。打印查看如下:
{"loaded":0,"total":3080,"speed":0,"percent":0} - 防坑指南:
putObject方法中的匿名函数统一换成箭头函数,否则“ this.percent ”无法指向data()函数中的percent变量,导致进度条不变化 - 上传成功就会得到data对象,访问data对象的Location属性就能拿到图片的非完整在线地址(没有协议名),手动拼接协议名后,得到完整可用的在线地址,随后存入imageUrl变量,实现在上传框预览头像 核心代码如下:
upload(e) {
// console.log(e)
// 如果文件对象不存在,退出函数
if (e.file === false) return
this.showProgress = true // 显示上传进度条
// 引入
var COS = require('cos-js-sdk-v5')
// new一个实例对象
var cos = new COS({
SecretId: 'AKIDhEUUDfspVv60dz7yZuxkGVTBcmfIekZ8',
SecretKey: 'MSHp78sLXS6TZOxFG2XliPpxoOimY4KJ'
})
// 调用cos实例的putObject()方法,实现文件的上传
cos.putObject({
Bucket: 'yoyo-1309207666', /* 存储桶名称 */
Region: 'ap-nanjing', /* 存储桶所在地域,必须字段 */
Key: e.file.name, /* 上传文件的名字 */
StorageClass: 'STANDARD',
Body: e.file, // 上传的文件对象File
onProgress: (progressData) => {
// 转换为整数 el-progress 要求percent属性为整数
this.percent = progressData.percent * 100
}
}, (err, data) => {
this.showProgress = false // 隐藏上传进度条
console.log(err || data)
if (data.statusCode === 200) {
// 手动拼接协议名后,得到完整可用的在线地址
this.imageUrl = 'https://' + data.Location
// 将完整的图片在线地址,传给父组件。因为这个子组件只是上传框,“ 保存更新 ”按钮在父组件中,是在父组件中点击该按钮,然后将在线地址发送给我们的服务器
this.$emit('uploadTOCosSuccess', this.imageUrl)
}
})
}
第四步:父组件接收头像地址
核心代码如下:
<!-- UploadImg 上传头像组件已经全局注册,使用时无需单独引入和注册 -->
<UploadImg @uploadTOCosSuccess="gotUrl" />
<el-button type="primary" @click="sendToOurServerHandler">上传</el-button>
data() {
return {
// 存完整的头像在线地址
completeUrl: "",
};
},
methods: {
// uploadTOCosSuccess自定义事件的回调,completeUrl变量为上传在线图片地址到我们的服务器做准备
gotUrl(completeUrl) {
this.completeUrl = completeUrl;
},
sendToOurServerHandler() {
// 点击确定后,将在线地址发给我们的服务器。暂无后端接口,无法演示
},
**疑问:**如何让已上传的图片回显在上传框中?
- 首先,发请求将头像在线地址传给我们的服务器;
- 然后,再发请求,让服务器将在线地址发给我们。父组件收到响应回来的在线地址,然后将在线地址传给子组件(异步,采用watch监听),子组件接收后将数据设定为src属性值就能回显
- 头像多处频繁使用,应该将在线地址存在vuex中