配合腾讯云Cos封装一个上传图片组件

一、需求分析

该组件需要满足以下要求:

  1. 可以显示传入的图片地址
  2. 可以删除传入的图片地址
  3. 可以上传图片到云服务器
  4. 上传到腾讯云之后,可以返回图片地址,显示
  5. 上传成功之后,可以回调成功函数

image-20200806174643390

从上图中,我们可以看到,实际上是有两种场景的,本地场景和已经上传的场景

二、安装 JavaScript SDK

1
$ npm i cos-js-sdk-v5 --save

三、实例化上传 sdk

名称 描述
SecretId 开发者拥有的项目身份识别 ID,用以身份认证,可在 API 密钥管理 页面获取
SecretKey 开发者拥有的项目身份密钥,可在 API 密钥管理 页面获取

注意,为了方便,这里直接将参数放置在前端代码中存储,但是腾讯云本身是不建议这么做的,因为**敏感信息**放在前端很容易被捕获。

正确的做法应该是,通过网站调用接口换取敏感信息

相关文档

1
2
3
4
var cos = new COS({
SecretId: "COS_SECRETID", // 身份识别 ID
SecretKey: "COS_SECRETKEY", // 身份密钥
});

四、准备组件并注册

新建上传图片组件 src/components/ImageUpload/index.vue

上传组件,我们可以沿用 element 的 el-upload 组件,并且采用照片墙的模式 list-type="picture-card"

放置 el-upload 组件

1
2
3
4
5
<template>
<el-upload list-type="picture-card">
<i class="el-icon-plus" />
</el-upload>
</template>

全局注册组件

1
2
3
4
5
6
import ImageUpload from "./ImageUpload";
export default {
install(Vue) {
Vue.component("ImageUpload", ImageUpload); // 注册导入上传组件
},
};

五、上传动作调用上传腾讯云

腾讯云文档地址

1、上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 进行上传操作
upload(params) {
// console.log(params.file)
if (params.file) {
// 执行上传操作
cos.putObject({
Bucket: 'shuiruohanyu-106-1302806742', // 存储桶
Region: 'ap-beijing', // 地域
Key: params.file.name, // 文件名
Body: params.file, // 要上传的文件对象
StorageClass: 'STANDARD' // 上传的模式类型 直接默认 标准模式即可
// 上传到腾讯云 =》 哪个存储桶 哪个地域的存储桶 文件 格式 名称 回调
}, function(err, data) {
// data返回数据之后 应该如何处理
console.log(err || data)
})
}
}

2、上传成功后处理返回数据

确定要上传记录 id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
beforeUpload(file) {
// 先检查文件类型
const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png']
if (!types.some(item => item === file.type)) {
// 如果不存在
this.$message.error('上传图片只能是 JPG、GIF、BMP、PNG 格式!')
return false // 上传终止
}
// 检查文件大小 5M 1M = 1024KB 1KB = 1024B
const maxSize = 5 * 1024 * 1024
if (file.size > maxSize) {
// 超过了限制的文件大小
this.$message.error('上传的图片大小不能大于5M')
return false
}
// 已经确定当前上传的就是当前的这个file了
this.currentFileUid = file.uid
return true // 最后一定要return true
},

处理返回的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 进行上传操作
upload(params) {
// console.log(params.file)
if (params.file) {
// 执行上传操作
cos.putObject({
Bucket: 'shuiruohanyu-106-1302806742', // 存储桶
Region: 'ap-beijing', // 地域
Key: params.file.name, // 文件名
Body: params.file, // 要上传的文件对象
StorageClass: 'STANDARD' // 上传的模式类型 直接默认 标准模式即可
// 上传到腾讯云 =》 哪个存储桶 哪个地域的存储桶 文件 格式 名称 回调
}, (err, data) => {
// data返回数据之后 应该如何处理
console.log(err || data)
// data中有一个statusCode === 200 的时候说明上传成功
if (!err && data.statusCode === 200) {
// 此时说明文件上传成功 要获取成功的返回地址
// fileList才能显示到上传组件上 此时我们要将fileList中的数据的url地址变成 现在上传成功的地址
// 目前虽然是一张图片 但是请注意 我们的fileList是一个数组
// 需要知道当前上传成功的是哪一张图片
this.fileList = this.fileList.map(item => {
// 去找谁的uid等于刚刚记录下来的id
if (item.uid === this.currentFileUid) {
// 将成功的地址赋值给原来的url属性
return { url: 'http://' + data.Location, upload: true }
// upload 为true 表示这张图片已经上传完毕 这个属性要为我们后期应用的时候做标记
// 保存 => 图片有大有小 => 上传速度有快又慢 =>要根据有没有upload这个标记来决定是否去保存
}
return item
})
// 将上传成功的地址 回写到了fileList中 fileList变化 =》 upload组件 就会根据fileList的变化而去渲染视图
}
})
}
}

我们在 fileList 中设置了属性为 upload 为 true 的属性,表示该图片已经上传成功了,如果 fileList 还有 upload 不为 true 的数据,那就表示该图片还没有上传完毕

六、上传的进度条显示

放置进度条

1
<el-progress v-if="showPercent" style="width: 180px" :percentage="percent" />

通过腾讯云 sdk 监听上传进度

1
2
3
4
5
6
7
8
9
10
11
12
cos.putObject({
// 配置
Bucket: 'laogao-1302806742', // 存储桶名称
Region: 'ap-guangzhou', // 存储桶地域
Key: params.file.name, // 文件名作为key
StorageClass: 'STANDARD', // 此类写死
Body: params.file, // 将本地的文件赋值给腾讯云配置
// 进度条
onProgress: (params) => {
this.percent = params.percent * 100
}
}

七、完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<template>
<!-- 上传组件 -->
<div>
<!-- :class="{ class名称:布尔值 }" -->
<!-- el-upload之所以能够显示图片 是因为 fileList中有值 -->
<el-upload
list-type="picture-card"
:file-list="fileList"
:on-preview="preview"
:on-remove="handleRemove"
:on-change="changeFile"
:before-upload="beforeUpload"
:http-request="upload"
action="#"
:class="{ disabled: fileComputed }"
>
<i class="el-icon-plus" />
</el-upload>
<el-progress
v-if="showPercent"
:percentage="percent"
style="width: 180px"
/>

<!-- 预览图片 -->
<el-dialog :visible.sync="showDialog" title="图片预览">
<img :src="imgUrl" alt="" style="width:100%" />
</el-dialog>
</div>
</template>

<script>
import COS from "cos-js-sdk-v5";
const cos = new COS({
SecretId: "", // 身份识别 ID
SecretKey: "", // 身份密钥
});
export default {
props: {
limit: {
type: Number,
default: 1,
},
},
data() {
return {
showPercent: false, // 控制进度条的显示和隐藏
percent: 0, // 当前的进度
showDialog: false, // 默认隐藏
imgUrl: "",
fileList: [],
};
},
computed: {
// 只要该计算属性为true 就表示 我们需要隐藏上传按钮
fileComputed() {
return this.fileList.length === this.limit;
},
},
methods: {
preview(file) {
this.imgUrl = file.url;
this.showDialog = true;
},
// file就是要删除的file
handleRemove(file) {
// 根据file中uid将当前的fileList中的数据进行移除
this.fileList = this.fileList.filter((item) => item.uid !== file.uid);
// filter方法会得到一个新的数组
},
// 不能够一味 的进行push 因为该函数会被多次调用 fileList其实就是当前最新的文件列表
changeFile(file, fileList) {
// this.fileList = [...fileList]
this.fileList = fileList.map((item) => item);
},
// 上传之前检查
beforeUpload(file) {
// console.log(file)
// 要开始做文件上传的检查了
// 文件类型 文件大小
const types = ["image/jpeg", "image/gif", "image/bmp", "image/png"];
if (!types.includes(file.type)) {
this.$message.error("上传图片只能是 JPG、GIF、BMP、PNG 格式!");
return false;
}
// 文件大小
const maxSize = 25 * 1024 * 1024;
if (maxSize < file.size) {
this.$message.error("图片最大的大小为5M");
return false;
}
return true; // 要返回true
},
// 上传到腾讯云
// 自定义上传动作
upload(params) {
// params中的file就是要上传的图片文件
// console.log(params.file)
if (params.file) {
this.showPercent = true; // 显示进度条
// 上传对象到腾讯云
cos.putObject(
{
Bucket: "shuiruohanyu-1302806742" /* 每个人的存储桶名称 */,
Region: "ap-nanjing" /* 存储桶所在地域,必须字段 */,
Key: params.file.name /* 文件名称 */,
StorageClass: "STANDARD", // 固定值
Body: params.file, // 上传文件对象
onProgress: (progressData) => {
// console.log(progressData.percent * 100)
this.percent = progressData.percent * 100;
},
},
(err, data) => {
console.log(err);
if (data.statusCode === 200 && data.Location) {
// 认为此时上传成功
// 需要知道当前的这个地址是谁的url地址
// params.file.uid => 当前上传文件的标识 如果找到了一一样的uid 就表示他们是一张图片
console.log(this.fileList);
// 这样相当于将原来的旧本地地址换成了新地址
this.fileList = this.fileList.map((item) => {
// 将本地的地址替换成线上已经放在腾讯云之后的地址
if (item.uid === params.file.uid) {
// upload 为true的意思是 表示这张图片 已经上传过了 已经不是本地图片了
return { url: "http://" + data.Location, upload: true };
}
return item;
});
this.showPercent = false; // 关闭进度条
this.percent = 0; // 将进度归0
}
}
);
}
},
},
};
</script>

<style>
.disabled .el-upload--picture-card {
display: none;
}
</style>

*通过上面的代码,我们会发现,我们把上传之后的图片信息都给了**fileList数据,那么在应用时,就可以直接获取该实例的fileList数据即可***

八、使用

1
2
3
4
5
6
7
8
<el-row class="inline-info">
<el-col :span="12">
<el-form-item label="员工头像">
<!-- 放置上传图片 -->
<image-upload ref="staffPhoto" />
</el-form-item>
</el-col>
</el-row>

1、读取时赋值

1
2
3
4
5
6
7
async getUserDetailById() {
this.userInfo = await getUserDetailById(this.userId)
if (this.userInfo.staffPhoto) {
// 这里我们赋值,同时需要给赋值的地址一个标记 upload: true
this.$refs.staffPhoto.fileList = [{ url: this.userInfo.staffPhoto, upload: true }]
}
},

2、更新图片时获取图片内容

1
2
3
4
5
6
7
8
9
10
11
12
async  saveUser() {
// 去读取 员工上传的头像
const fileList = this.$refs.staffPhoto.fileList // 读取上传组件的数据
if (fileList.some(item => !item.upload)) {
// 如果此时去找 upload为false的图片 找到了说明 有图片还没有上传完成
this.$message.warning('您当前还有图片没有上传完成!')
return
}
// 通过合并 得到一个新对象
await saveUserDetailById({ ...this.userInfo, staffPhoto: fileList && fileList.length ? fileList[0].url : '' })
this.$message.success('保存基本信息成功')
}
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2023-2025 congtianfeng
  • 访问人数: | 浏览次数: