Vue3封装el-upload
前言
在Vue3项目中,如果我们想上传图片一般可以利用element-ui中的el-upload
,为了避免代码的重复,我们可以自己封装一个图片上传组件。
其中,主要实现思想为前端利用el-upload组件选择上传的图片,并利用其http-request
属性来自定义函数来实现文件上传请求:该请求函数使用七牛云的对象存储,在通过后端得到的上传凭证token后来实现文件上传。
后端代码
使用express框架,获取七牛云上传凭证并响应给前端
项目结构
- routes
|- token.js
|- index.js
- app.js
- config.js
- package.json
安装七牛云的SDK:
npm i qiniu
获取上传凭证
编写获取上传凭证的相关代码:
/* config.js */
const qiniu = require('qiniu')
// 创建上传凭证
const accessKey = '*****' // 这里填写七牛云的accessKey
const secretKey = '*****'// 这里填写七牛云的secretKey
const mac = new qiniu.auth.digest.Mac(accessKey, secretKey)
const options = {
scope: '*****', // 这里填写七牛云空间名称
expires: 60 * 60 * 24 * 7 // 这里是凭证的有效时间,默认是一小时
}
const putPolicy = new qiniu.rs.PutPolicy(options)
const uploadToken = putPolicy.uploadToken(mac)
module.exports = {
uploadToken
}
配置路由
token.js
const tokenRouter = require('express').Router()
const qnconfig = require('../config') // 引入七牛云配置
tokenRouter.get('/qiniu', (req, res, next) => {
res.status(200).send(qnconfig.uploadToken)
})
module.exports = tokenRouter
index.js
const token = require('./token')
module.exports = routes = (app) => {
app.use('/token', token) // 可以通过/token/qiniu的方式获取上传凭证
}
项目启动
const express = require('express')
const bodyparse = require('body-parser')
const routers = require('./route')
// 创建服务
const app = express()
// 解析数据
app.use(bodyparse.json())
// 路由
routes(app)
// 监听3000端口
app.listen(3000, () => {
console.log('this server are running on localhost:3000!')
})
使用命令node app.js
启动项目,这时访问http://localhost:3000/token/qiniu
即可获取上传凭证了。
前端代码
配置跨域
由于前后端项目运行在不同的端口,因此需要解决跨域问题,这里在vite.config.js中解决如下:
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
父组件使用
我们希望子组件上传图片得到一串url后父组件能接受到,并且在展示上传图片时其尺寸应能指定或者有默认值。
<template>
<Upload :url="imageUrl" @upload="changeUrl" />
</template>
<script setup>
import Upload from '@/components/Upload.vue'
import { ref } from 'vue'
const imageUrl = ref('')
const changeUrl = (url) => {
imageUrl.value = url
}
</script>
封装组件Upload.vue
这里只是简单使用axios,没有对其进行封装。
<template>
<!- action="https://upload-z2.qiniup.com":每个地区访问域名不同,具体可通过 https://developer.qiniu.com/kodo/1671/region-endpoint-fq 查看 ->
<el-upload
class="avatar-uploader"
action="https://upload-z2.qiniup.com"
:show-file-list="false"
:http-request="up2qiniu"
:before-upload="beforeUpload"
>
<img
v-if="props.url"
:src="props.url"
class="avatar"
:style="'width: ' + props.width + 'px;' + 'height: ' + props.height + 'px;'"
/>
<el-icon
v-else
class="avatar-uploader-icon"
:style="'width: ' + props.width + 'px;' + 'height: ' + props.height + 'px;'"
><Plus
/></el-icon>
</el-upload>
</template>
<script setup>
import { ref } from 'vue'
import { getQiniuToken } from '../api/token'
import axios from 'axios'
import { ElMessage } from 'element-plus'
const qiniuaddr = 'rlr92qkze.hn-bkt.clouddn.com' // 这里是七牛云存储对象中的CDN域名
const imageUrl = ref('')
// 父组件传值时,须有图片的url;其次可选择图片的宽高(默认都为180)
const props = defineProps({
url: String,
width: {
type: Number,
default: 180
},
height: {
type: Number,
default: 180
}
})
const emit = defineEmits(['upload'])
const beforeUpload = (rawFile) => {
if (rawFile.type !== 'image/jpg' && rawFile.type !== 'image/png') {
ElMessage.error('图片格式应该是png或jpg')
return false
} else if (rawFile.size / 1024 / 1024 > 2) {
ElMessage.error('图片大小应该小于2MB')
return false
}
return true
}
/**
* 上传图片至七牛云
* @param {*} req
*/
const up2qiniu = (req) => {
const config = {
headers: { 'Content-Type': 'multipart/form-data' }
}
const fileType = req.file.type === 'image/png' ? 'png' : 'jpg'
// 重命名要上传的文件
const keyname = 'blog' + new Date().getTime() + '.' + fileType
axios.get('/api/token/qiniu').then(res => {
const formdata = new FormData()
formdata.append('file', req.file)
formdata.append('token', res.data)
formdata.append('key', keyname)
// 获取到凭证之后再将文件上传到七牛云空间
axios.post('https://upload-z2.qiniup.com', formdata, config).then((res) => {
imageUrl.value = 'http://' + qiniuaddr + '/' + res.data.key
emit('upload', imageUrl.value) // 向父组件传递图片的url
})
})
}
</script>
<style lang="scss" scoped>
.avatar-uploader .avatar {
width: 360px;
height: 180px;
display: block;
}
.avatar-uploader :deep(.el-upload) {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
.avatar-uploader :deep(.el-upload:hover) {
border-color: var(--el-color-primary);
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 360px;
height: 180px;
text-align: center;
}
</style>
推荐阅读
单文件组件下的vue,可以擦出怎样的火花
与时俱进吧,看着 vue3 和 vite,虽然不会用,但还是心痒痒,然后就把原先基于 vue@2 的实现做了重构。不周之处,大家见谅!下面关于过期的内容,我就用删除线标记了。
leftstick赞 64阅读 45.2k评论 18
Vue中的diff算法
diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有 O(n)。diff算法的在很多场景下都有应用,例如在 vue 虚拟 dom 渲染成真实 dom 的新旧 VNode 节点比较更新时...
款冬赞 27阅读 13.4k评论 7
给我实现一个前端的 Excel 导入和导出功能
前言【负责人 A】:现在报表部分基于接口的 Excel 的导入和导出功能有点慢,前端这边能不能实现一下这个功能,然后我们在比对看看效果!【切图仔 B】: 接口这边不能优化一下吗?比如排查下慢的原因什么的。【负...
熊的猫赞 19阅读 2.6k
vue-property-decorator使用手册
@Component 装饰器可以接收一个对象作为参数,可以在对象中声明 components ,filters,directives等未提供装饰器的选项,也可以声明computed,watch等
似曾相识赞 17阅读 29.1k评论 7
一个开源vue网站博客,nuxt开源网站,前后端分离项目
开媛笔记,基于nuxt ssr首屏服务器端渲染 。用于分享、记录、交流和学习,希望可以帮助到小伙伴们。同时网站在不断更新,创造属于猿(媛)的世界 -$Bao Yalong ..Let's Go! [链接]
jigsaw赞 16阅读 8.4k评论 3
你知道前端水印功能是怎么实现的吗?
前一段时间由于项目需要实现水印功能,于是去了解了相关的内容后,基于 Vue 的实现了一个 v-watermark 指令完成了对应的功能,其实整体内容并不复杂!
熊的猫赞 14阅读 1.7k
2022 你还不会微前端吗 (上) — 从巨石应用到微应用
微前端系列分为 上/下 两篇,本文为 上篇 主要还是了解微前端的由来、概念、作用等,以及基于已有的微前端框架进行实践,并了解微前端的核心功能所在,而在下篇 2022 你还不会微前端吗 (下) — 揭秘微前端核心原理...
熊的猫赞 14阅读 1.6k
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。