图片存储方案介绍
目标
了解主流的图片存储方案
两种常见方案
方案一:存到自己公司购买的服务器上
优点:好控制
缺点:成本高由于图片都存放到自己的服务器上,占据空间很大
方案二:存到三方云服务器(阿里云,七牛云,腾讯云)
各种云有专门的为图片存储提供的云服务器,我们自己的服务器只存储图片地址即可
腾讯云cos申请配置
目标
使用现成的腾讯云服务创建一个免费的云存储。
创建账号并实名认证
在腾讯云中创建帐号并实名认证
开通对象存储
(实名认证,视频校验,居住地址,......)
创建存储桶
设置cors规则
在存储桶列表中,选中存储桶
在左侧的菜单中选安全管理
因为我们是在测试上传,全部容许上传即可,真正的生产环境需要单独配置具体的域名和操作方法
配置云API秘钥
服务器属于个人的,需要一定的权限才能自由上传图片,这个负责权限验证的其实就是秘钥。拥有秘钥是进行上传的基础条件。
秘钥配置
API密钥管理
安全性提示
实际工作中,秘钥属于敏感信息,不能直接放到前端存储,容易产生安全问题,更好的做法是把秘钥交给后端管理,前端通过调用接口先获取秘钥,有了秘钥之后再进行上传操作
以上我们就完成了,所有的准备工作~
图片上传组件-封装组件-基本思路
目标
封装一个组件,用来把图片上传到cos中。
思路
组件有两个功能:
- 展示图片(初始显示图片)
- 修改图片
图片上传组件-封装组件-upload二次封装
目标
基于elementUI的upload组件封装一个通用的上传组件供业务组件使用
需求理解
前端主动发起图片上传使用的是三方的腾讯云上传接口,前端得到一个已经上传完毕的图片地址,然后把这个地址当成一个接口字段 传给我们自己的后端服务
详情页
新建公共上传组件
我们的上传功能是基于element上传组件的二次开发,先准备好我们需要的elementUI上传组件,根据我们具体的业务需求选取一个合适的样例代码
src/components/UploadImg
<template>
<div>
<el-upload
class="avatar-uploader"
action="#"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
:http-request="upload"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
</div>
</template>
<script>
export default {
data() {
return {
imageUrl: ''
}
},
methods: {
upload(file) {
console.log(file)
},
handleAvatarSuccess(res, file) {
this.imageUrl = URL.createObjectURL(file.raw)
},
beforeAvatarUpload(file) {
const isPNG = file.type === 'image/png'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isPNG) {
this.$message.error('上传头像图片只能是 PNG 格式!')
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!')
}
return isPNG && isLt2M
}
}
}
</script>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
预览,在首页模块进行组件预览显示
自定义上传配置
关键属性::http-request="upload" action="#"
使用自定义行为覆盖默认上传,注意一旦设置自定义上传行为之后,所有的上传操作都需要自己实现,比如数据处理,上传成功之后的后续操作,on-success钩子函数也不会继续触发
<template>
<div>
<!--
show-file-list: 是否显示上传的文件列表
action: '#' 用来指定文件要上传的地址,由于我们需要定制上传动作
这里设为#
:http-request:自定义上传行为(重点)
on-success: 上传成功之后的回调
before-upload: 上传之前的检查
-->
<el-upload
class="avatar-uploader"
action="#"
:show-file-list="false"
:before-upload="beforeAvatarUpload"
:http-request="upload"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
</div>
</template>
<script>
export default {
methods:{
upload(params){
console.log(params)
}
}
}
</script>
全局注册
src/components/index.js
// 全局注册组件
// 省略其他....
+ import UploadImg from '@/components/UploadImg'
// 1.定义插件(拓展Vue的功能)
const MyPlugin = {
install(Vue) {
// 省略其他....
+ Vue.component(UploadImg.name, UploadImg)
}
}
export default MyPlugin
测试使用
主页中测试(其他页面也可以)
图片上传组件-封装组件-上传图片到腾讯云
前置基础:
1.在云服务器上的准备:申请cos服务器,配置密钥,设置cors访问
2.在代码层面的准备:
(1) 下载一个官方提供的操作cos服务的包(cos-js-sdk-v5)
(2) 用自己的密钥去实例化cos
(3) 具体做上传
目标
用前边注册的对象存储功能,将图片上传到腾讯云,根据cos的上传API实现上传功能
安装依赖
在项目中安装依赖
npm i cos-js-sdk-v5 --save
实例化cos对象
在src/components/UploadImg中
// 下面的代码是固定写法
const COS = require('cos-js-sdk-v5')
// 填写自己腾讯云cos中的key和id (密钥)
const cos = new COS({
SecretId: 'xxx', // 身份识别ID
SecretKey: 'xxx' // 身份秘钥
})
上边用到的SecretId和SecretKey在这里找到:
使用cos对象完成上传
主要是用cos.putObjectapi来完成上传功能,代码如下:
upload(res) {
if (res.file) {
// 执行上传操作
cos.putObject({
Bucket: 'xxxxxx', /* 存储桶 */
Region: 'xxxx', /* 存储桶所在地域,必须字段 */
Key: res.file.name, /* 文件名 */
StorageClass: 'STANDARD', // 上传模式, 标准模式
Body: res.file // 上传文件对象
}, (err, data) => {
console.log(err || data)
// 上传成功之后
if (data.statusCode === 200) {
this.imageUrl = `https:${data.Location}`
}
})
}
}
上边的Bucket和Region在这里可以找到
图片上传组件-封装组件-补充上传进度条
目标
给出用户更好的上传体验,显示当前上传进度
思路
- 在上传时,cos支持配置onProgress回调,在这个回调函数中可以拿到当前上传的进度。
cos.putObject({
// 省略其他...
onProgress: (progressData) => {
console.log(JSON.stringify(progressData))
}
}
- 进度条: 现成的组件:el-progress
添加布局
补充进度条组件:el-progress
<el-upload
class="avatar-uploader"
action="#"
:show-file-list="false"
:before-upload="beforeAvatarUpload"
:http-request="upload"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon" />
+ <el-progress type="circle" :percentage="25" class="progress" />
</el-upload>
<style>
.progress {
position: absolute;
display: flex;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
background: #fff;
}
</style>
实现功能
补充数据项:
data () {
return {
// ...
percent: 0, // 上传进度
showProgress: false // 是否展示进度条
}
}
核心逻辑:在onProgress钩子函数里动态修改负责显示进度的percent属性即可
更新数据项值
upload(params) {
if (params.file) {
// 显示进度条
+ this.showProgress = true
// 执行上传操作
cos.putObject({
Bucket: 'chaichai-1305124340', /* 存储桶 */
Region: 'ap-beijing', /* 存储桶所在地域,必须字段 */
Key: params.file.name, /* 文件名 */
StorageClass: 'STANDARD', // 上传模式, 标准模式
Body: params.file, // 上传文件对象
onProgress: (progressData) => {
console.log(JSON.stringify(progressData))
+ this.percent = progressData.percent * 100
}
}, (err, data) => {
// 隐藏进度条
+ this.showProgress = false
console.log(err || data)
if (data.statusCode === 200) {
this.imageUrl = `https:${data.Location}`
}
})
}
}
视图
<el-progress v-if="showProgress" :percentage="percent" />
用户详情页中使用上传组件-v-model
目标
我们希望达成的目标是:在upload-img上可以使用v-model做双向绑定。如下:
<el-form-item label="员工头像">
<!-- <img :src="userInfo.staffPhoto"> -->
<!-- 放置上传图片 -->
<upload-img v-model="imageUrl" />
</el-form-item>
upload-img: 自定义的组件, v-model:双向绑定。
复习v-model
v-model是一个语法糖。
<com v-model="imageUrl" />
上面的写法等价于:
<com :value="imageUrl" @input="val=>imageUrl=val" />
1. 给子组件内部传一个名为value的属性,属性值就是绑定的数据项
2. 在子组件监听一个名为input的事件,回调函数是:将收到的参数值保存到数据项中
代码
父组件
v-model绑定值
<upload-img v-model="userInfo.staffPhoto" />
子组件(图片上传组件)
- 定义props value来接收父组件传入的图片地址
- 直接用value来显示图片(把原来 imageUrl删除了)
- 在上传成功之后,this.$emit('input', 新地址), 抛给父组件,达到更新父组件中v-model绑定数据项
<template>
<!--
show-file-list: 是否显示上传的文件列表
action: '#' 用来指定文件要上传的地址,由于我们需要定制上传动作
(我们要上传到腾讯云cos)
这里设为#
:http-request:自定义上传行为(重点)
on-success: 上传成功之后的回调
before-upload: 上传之前的检查
:on-success="handleAvatarSuccess"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload"
-->
<el-upload
class="avatar-uploader"
action="#"
:http-request="upload"
:show-file-list="false"
>
+ <img v-if="value" :src="value" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon" />
<el-progress
v-if="isShow"
:percentage="percentage"
class="progress"
/>
</el-upload>
</template>
<script>
// 下面的代码是固定写法
const COS = require('cos-js-sdk-v5')
// 填写自己腾讯云cos中的key和id (密钥)
const cos = new COS({
SecretId: 'AKIDEI7fxzkFTXLtBD**********', // 身份识别ID 这俩填你们自己的
SecretKey: '91cx0nJabLo1yf5D5**************' // 身份秘钥
})
export default {
name: 'UploadImg',
+ props: {
// 从父组件传递过来的v-model对应的值
value: { type: String, default: '' }
},
data() {
return {
// imageUrl: '',
percentage: 0, // 上传进度
isShow: false // 进度条是否可见
}
},
methods: {
upload(res) {
console.log('当前要上传的文件是', res.file)
if (res.file) {
// 执行上传操作
this.isShow = true // 进度条可见
cos.putObject({
Bucket: 'tj03-1258898967', /* 存储桶 */
Region: 'ap-nanjing', /* 存储桶所在地域,必须字段 */
Key: res.file.name, /* 文件名 */
StorageClass: 'STANDARD', // 上传模式, 标准模式
Body: res.file, // 上传文件对象
onProgress: (progressData) => {
console.log('上传文件进度.....', JSON.stringify(progressData))
this.percentage = progressData.percent * 100
}
}, (err, data) => {
this.isShow = false // 进度条不可见
console.log(err || data)
// 上传成功之后
if (data.statusCode === 200) {
const urlImg = `https://${data.Location}`
+ this.$emit('input', urlImg)
}
})
}
}
}
</script>
小结
图片上传组件整个工作过程
全天小结
- excel 导出
- 用xlsx库,把excel文件的内容读出来到浏览器的内存中。
- 数据格式化(后端要求的数据格式与直接读入的内容格式不同)
- 调用后端接口
- 员工详情页
- 把图片上传到腾讯云COS (云cloudy 对象 Object S 存储store )
- 账号申请,认证
- 购买COS服务
- 建立存储桶,设置CORS,生成API秘钥,用它提供的API做文件上传
- 图片上传组件封装
- element-ui: el-upload组件, el-progress组件
- v-model
在员工列表中显示头像
目标
在表格中显示员工头像
思路
通过自定义列,将图片显示在表格中
操作
在employee.vue中的table上补充一列
<el-table-column label="头像">
<template slot-scope="scope">
<img :src="scope.row.staffPhoto" />
</template>
</el-table-column>
此时可能部分图片无法正常显示(原因是图片本身地址有误,或者就是没有设置图片)
封装全局图片组件
目标
封装全局图片组件,实现两个效果:
- 没有src属性时,有一个默认图片
- 统一样式
基本思路
用户如何使用?
可以在el-image的基础之上,做进一步开发。
创建组件
在src/components目录下创建组件。
具体目录结构如下
|-components
|-ImageHolder
|-index.vue # 组件本身
|-head.jpg # 默认头像,当没有图像需要显示时,使用这张图
代码
src\components\ImageHolder\index.vue
<template>
<!-- el-image组件 :https://element.eleme.io/#/zh-CN/component/image -->
<el-image :src="src" class="img-container">
<!-- error插槽:设置如果出错,要显示的内容 -->
<template #error>
<!-- 静态资源就近维护:把head.jpg这张图放在这个组件的目录下 -->
<img src="./head.jpg" alt="">
</template>
</el-image>
</template>
<script>
export default {
// 定义如何从父组件中接收数据
props: {
src: {
type: String,
default: ''
}
}
}
</script>
<style scoped lang="scss">
// 样式穿透, 有三种写法:
// 1. >>> 在less中使用
// 2. /deep/ 在less中使用
// 3. ::v-deep 在scss中使用
.img-container {
border-radius: 50%;
::v-deep img{
width: 100px;
height: 100px;
}
}
</style>
全局注册
在src/components/index.js中注册全局组件
// 定义全局组件PageTools
// 省略其它...
+ import ImageHolder from '@/components/ImageHolder/index.vue'
const MyPlugin = {
install: function(Vue) {
// 省略其它...
+ Vue.component('ImageHolder', ImageHolder)
}
}
export default MyPlugin
在业务组件中使用
在src\views\employees\employees.vue中,用上面定义的组件来显示员工图像
<el-table-column label="头像">
<template slot-scope="scope">
<image-holder :src="scope.row.staffPhoto" />
</template>
</el-table-column>
src就是传递给子组件的prop。