前言

在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 = '*****' // 这里填写七牛云的accessKeyconst secretKey = '*****'// 这里填写七牛云的secretKeyconst 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>