700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > vue+el-upload组件封装(图片 文件上传至oss阿里云)

vue+el-upload组件封装(图片 文件上传至oss阿里云)

时间:2023-01-21 16:15:02

相关推荐

vue+el-upload组件封装(图片 文件上传至oss阿里云)

1、安装ali-oss

npm install ali-oss --save

2、oss方法封装

新建utils/ali-oss-upload.js文件(代码如下)

const OSS = require('ali-oss')// 文档链接:/document_detail/32068.html?spm=a2c4g.11186623.6.1291.86b3c107IHjkTR/*** 阿里 OSS 服务 OSS 对象* [accessKeyId] {String}:通过阿里云控制台创建的AccessKey。* [accessKeySecret] {String}:通过阿里云控制台创建的AccessSecret。* [stsToken] {String}:使用临时授权方式,详情请参见使用 STS 进行临时授权。* [bucket] {String}:通过控制台或PutBucket创建的bucket。* [endpoint] {String}:OSS域名。* [region] {String}:bucket所在的区域, 默认oss-cn-hangzhou。* [internal] {Boolean}:是否使用阿里云内网访问,默认false。比如通过ECS访问OSS,则设置为true,采用internal的endpoint可节约费用。* [cname] {Boolean}:是否支持上传自定义域名,默认false。如果cname为true,endpoint传入自定义域名时,自定义域名需要先同bucket进行绑定。* [isRequestPay] {Boolean}:bucket是否开启请求者付费模式,默认false。具体可查看请求者付费模式。* [secure] {Boolean}:(secure: true)则使用HTTPS,(secure: false)则使用HTTP,详情请查看常见问题。* [timeout] {String|Number}:超时时间,默认60s。*/const client = new OSS({region: '',accessKeyId: '',accessKeySecret: '',bucket: ''})/*** OSS 服务文件上传单个文件* 同步方式:基于 async 和 await 方式,异步编程同步化。* @param {String} objectName 可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。* @param {file} file 本地文件或者文件路径*/export async function ossPut(objectName, file) {try {const result = await client.put(objectName, file)// console.log(result)return result} catch (error) {// console.log(error)return error}}/*** OSS 服务文件上传单个文件* 异步方式:API 接口返回 Promise* @param {String} objectName 可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。* @param {file} file 本地文件或者文件路径*/export async function ossAsyncPut(objectName, file) {return new Promise((resolve, reject) => {client.put(objectName, file).then(res => {resolve(res)}).catch(error => {resolve(error)})})}/*** OSS 服务文件下载单个文件* 同步方式:基于 async 和 await 方式,异步编程同步化。* @param {String} objectName*/export async function ossGet(objectName) {try {const result = await client.get(objectName)// console.log(result)return result} catch (error) {// console.log(error)return error}}/*** OSS 服务文件下载单个文件* 异步方式:API 接口返回 Promise* @param {String} objectName*/export async function ossAsyncGet(objectName) {return new Promise((resolve, reject) => {client.get(objectName).then(res => {resolve(res)}).catch(error => {reject(error)})})}/*** OSS 服务文件删除单个文件* @param {String} objectName*/export async function ossDelete(objectName) {try {const result = await client.delete(objectName)return result} catch (error) {// console.log(error)return error}}

3、组件封装

图片上传

1) 涉及到图片转换的方法如下(utils/index.js)

/*** base64ToFileObject base64 码转 blob 二进制,再转 file 对象* @param {[type]} base64 [base64码]* @param {string} fileName [转码后 file 对象的名称]* @return {[type]} newFile[返回新的file对象]*/export function base64ToFileObject(base64, fileName = 'file') {const mime = base64.split(',')[0].match(/:(.*?);/)[1]base64 = base64.split(',')[1]base64 = window.atob(base64)const u8arr = new Uint8Array(base64.length)for (let i = 0; i < base64.length; i++) {u8arr[i] = base64.charCodeAt(i)}const blob = new Blob([u8arr], {type: mime })const suffix = mime.split('/')[1]return new File([blob], `${fileName}.${suffix}`, {type: mime })}/*** imgToBase64 压缩图片* @param {[type]} file[file对象 event.target.files[0]]* @param {[type]} maxWidth [最大宽度]* @param {[type]} maxHeight [最大高度]* @return {[type]} [description]*/export function imgToBase64(file, maxWidth, maxHeight) {return new Promise((resolve, reject) => {// 压缩图片需要的一些元素和对象const reader = new FileReader()const img = new Image()img.onload = function() {// 图片原始尺寸const originWidth = this.widthconst originHeight = this.height// 目标尺寸let targetWidth = originWidthlet targetHeight = originHeight// 图片尺寸超过400x400的限制if (originWidth > maxWidth || originHeight > maxHeight) {if (originWidth / originHeight > maxWidth / maxHeight) {// 更宽,按照宽度限定尺寸targetWidth = maxWidthtargetHeight = Math.round(maxWidth * (originHeight / originWidth))} else {targetHeight = maxHeighttargetWidth = Math.round(maxHeight * (originWidth / originHeight))}}// 缩放图片需要的canvasconst canvas = document.createElement('canvas')const context = canvas.getContext('2d')// canvas对图片进行缩放canvas.width = targetWidthcanvas.height = targetHeight// 清除画布context.clearRect(0, 0, targetWidth, targetHeight)// 图片压缩context.drawImage(img, 0, 0, targetWidth, targetHeight)const base64 = canvas.toDataURL('image/jpeg', 0.8) // 压缩后质量// const base64 = canvas.toDataURL()// console.log(canvas.toDataURL());resolve(base64)}reader.onload = function(e) {img.src = e.target.result}reader.readAsDataURL(file)})}

2)组件封装,新建uploadImg/index.vue文件,代码如下

<!--* @Description:图片上传* @Author: duyan* @Date: -10-27 19:44:02* @LastEditTime: -11-19 16:05:57* @LastEditors: duyan--><template><div class="uploadImg"><el-upload:class="(maxCount>imgLength)?'uploadImgContent':'uploadImgContentNone'":file-list="value":limit="maxCount":drag="isDrag":http-request="onUploadFile":before-upload="handleBeforeUpload":on-preview="handlePictureCardPreview":on-remove="handleRemove":on-error="handleError"v-bind="$attrs"action=""accept="image/png,image/jpg,image/jpeg"list-type="picture-card"v-on="$listeners"><i slot="default" class="el-icon-plus"/><div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过2M</div></el-upload><el-dialog :visible.sync="dialogVisible" width="100%" append-to-body><img :src="dialogImageUrl" height="100%" alt=""></el-dialog></div></template><script>import {imgToBase64, base64ToFileObject } from '@/utils'import {ossAsyncPut, ossDelete } from '@/utils/ali-oss-upload.js'import {v4 as uuidv4 } from 'uuid'export default {// 名字name: 'UploadImg',// 部件components: {},model: {prop: 'fileList'},// 静态props: {fileList: {type: Array,default: () => []},// 是否直接上传到 OSS 服务isPutOss: {type: Boolean,default: false},// 图片上传到 OSS 服务上的存储目录ossPathPrefix: {type: String,default: ''},// 图片上传数maxCount: {type: Number,default: 1},isDrag: {type: Boolean,default: false},// eslint-disable-next-line vue/require-default-propuploadSuccess: Function,// eslint-disable-next-line vue/require-default-propafterRead: Function},// 数据data() {return {dialogImageUrl: '',dialogVisible: false,imgLength: 0}},// 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;computed: {value: {get(val) {// eslint-disable-next-line vue/no-side-effects-in-computed-propertiesthis.imgLength = this.fileList.lengthreturn this.fileList},set(val) {this.imgLength = val.lengththis.$emit('input', val)}}},// 对象内部的属性监听,也叫深度监听watch: {},// 请求数据created() {},mounted() {},// 方法表示一个具体的操作,主要书写业务逻辑;methods: {// 图片上传onUploadFile(file) {// 上传完成this.afterRead && this.afterRead(file)// 上传ossthis.isPutOss && this.onUpload(file)},onUpload(file) {file.status = 'uploading'file.message = '上传中...'this.uploaded(file.file).then(response => {const {url } = responsefile.url = urlfile.status = 'done'file.message = '上传成功'this.value.push(file)this.$emit('input', this.value)// this.uploadSuccess(file)this.$forceUpdate()// 强制渲染}).catch(() => {file.status = 'failed'file.message = '上传失败'file.url = nullthis.$forceUpdate()})},/*** 文件上传至阿里 OSS 服务*/uploaded(file) {return new Promise((resolve, reject) => {const suffix = file.type.split('/')[1]const fileName = `${this.ossPathPrefix}/${uuidv4()}.${suffix}`ossAsyncPut(fileName, file).then(({res, url }) => {// console.log(res)// console.log(url)if (res && res.status === 200) {// console.log('上传成功')resolve({res, url })} else {// console.log('上传失败')reject(new Error('图片上传 OSS 服务失败!'))}})})},// 图片从oss上删除_delete(file) {console.log(file)if (file.url) {const startIndex = file.url.indexOf(this.ossPathPrefix)const objectName = file.url.substr(startIndex)ossDelete(objectName).then(res => {})}},handleBeforeUpload(file) {return new Promise((resolve, reject) => {const fileType = ['image/png', 'image/jpg', 'image/jpeg']if (!fileType.includes(file.type)) {this.$notify.warning({title: '警告',message: '请上传格式为image/png, image/jpg, image/jpeg的图片'})reject()}// 图片压缩后校验图片大小imgToBase64(file, 800, 800).then(base64 => {const resultFile = base64ToFileObject(base64)const isNeed = resultFile.size / 1024 < 200// console.log(resultFile)// console.log(resultFile.type, isNeed)if (!isNeed) {this.$notify.warning({title: '警告',message: '图片过大'})reject()}resolve()})})},handlePictureCardPreview(file) {this.dialogImageUrl = file.urlthis.dialogVisible = true},handleRemove(file, fileList) {this.value = fileListthis._delete(file)this.$forceUpdate()},handleError(err, file, fileList) {console.log('error:', err, file, fileList)}}}</script><style scoped lang="scss">.uploadImg{.uploadImgContent{::v-deep .el-upload{display: inline-block !important ;}}.uploadImgContentNone{::v-deep .el-upload{display: none !important;}}}::v-deep .el-dialog{margin: 5vh 0 !important;background: none;.el-dialog__headerbtn .el-dialog__close {color: #dddddd;border: 3px solid #dddddd;border-radius: 50%;font-size: 28px;}.el-dialog__body{height: calc( 100vh - 20vh );overflow: hidden;padding:0px 10px 10px 10px;text-align: center;}}</style>

文件上传封装

1) 文件类型使用及判断见此处

2) 新建uploadFile/index.vue文件,代码如下

<!--* @Description:文件上传* @Author: duyan* @Date: -10-27 19:44:02* @LastEditTime: -12-21 11:29:30* @LastEditors: duyan--><template><div class="uploadImg"><el-upload:class="(maxCount>imgLength)?'uploadImgContent':'uploadImgContentNone'":file-list="value":limit="maxCount":drag="isDrag":http-request="onUploadFile":before-upload="handleBeforeUpload":on-preview="handlePictureCardPreview":on-remove="handleRemove":on-error="handleError"v-bind="$attrs":accept="fileType":show-file-list="isShowFileList"action=""v-on="$listeners"><div v-show="value.length<maxCount"><!-- 模板下载 --><div v-if="templateDownload"><el-dropdown split-button type="success"><i class="el-icon-upload2"/>{{btnLabel }}<el-dropdown-menu slot="dropdown"><el-dropdown-item icon="el-icon-download"><!-- <div style="display: inline-block; height:32px;width:100px;background-color:#11b95c;border:transparent;text-align:center;line-height:32px;border-radius:3px;"> --><a :href="downloadUrl">下载导入模板</a><!-- </div> --></el-dropdown-item></el-dropdown-menu></el-dropdown></div><div v-else><el-button size="small" icon="el-icon-plus" type="primary">{{btnLabel }}</el-button><div slot="tip" class="el-upload__tip">只能上传{{fileType }}文件</div></div></div></el-upload></div></template><script>import {ossAsyncPut, ossDelete } from '@/utils/ali-oss-upload.js'import {v4 as uuidv4 } from 'uuid'export default {// 名字name: 'UploadImg',// 部件components: {},model: {prop: 'fileList'},// 静态props: {// 文件listfileList: {type: Array,default: () => []},// 是否直接上传到 OSS 服务isPutOss: {type: Boolean,default: false},// 文件上传到 OSS 服务上的存储目录ossPathPrefix: {type: String,default: ''},// 文件上传数maxCount: {type: Number,default: 1},isDrag: {type: Boolean,default: false},// 文件类型fileType: {type: String,default: () => '.pdf'},fileTypeList: {type: Array,default: () => {return ['application/pdf', 'application/doc', 'application/docx']}},// 按钮文字btnLabel: {type: String,default: '点击上传'},// 是否显示已上传文件列表isShowFileList: {type: Boolean,default: true},// 是否进行模板下载templateDownload: {type: Boolean,default: false},// 模板下载链接downloadUrl: {type: String,default: 'https://yzwy1-app.oss-cn-/communityModel.xlsx'},// eslint-disable-next-line vue/require-default-propuploadSuccess: Function,// eslint-disable-next-line vue/require-default-propafterRead: Function},// 数据data() {return {dialogImageUrl: '',dialogVisible: false,imgLength: 0}},// 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;computed: {value: {get(val) {// eslint-disable-next-line vue/no-side-effects-in-computed-propertiesthis.imgLength = this.fileList.lengthreturn this.fileList},set(val) {this.imgLength = val.lengththis.$emit('input', val)}}},// 对象内部的属性监听,也叫深度监听watch: {},// 请求数据created() {},mounted() {},// 方法表示一个具体的操作,主要书写业务逻辑;methods: {// 文件上传onUploadFile(file) {// 上传完成this.afterRead && this.afterRead(file)// 上传ossthis.isPutOss && this.onUpload(file)},onUpload(file) {file.status = 'uploading'file.message = '上传中...'this.uploaded(file.file).then(response => {const {url } = responsefile.url = urlfile.status = 'done'file.message = '上传成功'file.name = file.file.namethis.value.push(file)this.$emit('input', this.value)// this.uploadSuccess(file)this.$forceUpdate()// 强制渲染}).catch(() => {file.status = 'failed'file.message = '上传失败'file.url = nullthis.$forceUpdate()})},/*** 文件上传至阿里 OSS 服务*/uploaded(file) {return new Promise((resolve, reject) => {const suffix = file.type.split('/')[1]const fileName = `${this.ossPathPrefix}/${uuidv4()}.${suffix}`ossAsyncPut(fileName, file).then(({res, url }) => {// console.log(res)// console.log(url)if (res && res.status === 200) {// console.log('上传成功')resolve({res, url })} else {// console.log('上传失败')reject(new Error('文件上传 OSS 服务失败!'))}})})},// 文件从oss上删除_delete(file) {console.log(file)if (file.url) {const startIndex = file.url.indexOf(this.ossPathPrefix)const objectName = file.url.substr(startIndex)ossDelete(objectName).then(res => {})}},handleBeforeUpload(file) {return new Promise((resolve, reject) => {const fileType = this.fileTypeList// console.log('fileType----', fileType)// console.log('file----', file)if (!fileType.includes(file.type)) {this.$notify.warning({title: '警告',message: '请上传格式为' + this.fileType + '的文件'})reject()}resolve()})},handlePictureCardPreview(file) {this.dialogImageUrl = file.urlthis.dialogVisible = true},handleRemove(file, fileList) {this.value = fileListthis._delete(file)this.$forceUpdate()},handleError(err, file, fileList) {console.log('error:', err, file, fileList)}}}</script><style scoped lang="scss">.uploadImg{.uploadImgContent{::v-deep .el-upload{display: inline-block !important ;}}.uploadImgContentNone{::v-deep .el-upload{display: none !important;}}}::v-deep .el-dialog{margin: 5vh 0 !important;background: none;.el-dialog__headerbtn .el-dialog__close {color: #dddddd;border: 3px solid #dddddd;border-radius: 50%;font-size: 28px;}.el-dialog__body{height: calc( 100vh - 20vh );overflow: hidden;padding:0px 10px 10px 10px;text-align: center;}}.el-dropdown {vertical-align: top;}.el-dropdown + .el-dropdown {margin-left: 15px;}.el-icon-arrow-down {font-size: 12px;}</style>

4.引入使用

代码如下:

<!--* @Description:组件使用* @Author: duyan* @Date: -12-20 10:37:07* @LastEditTime: -12-31 15:30:11* @LastEditors: duyan--><template><div class="community-administrative"><!-- 上传本地返回 --><div style="line-height:35px;">上传表格文件本地返回:</div><upload-file:file-type="'.xls,.xlsx'":file-type-list="['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','application/vnd.ms-excel']"v-model="fileListFile":is-put-oss="false":btn-label="downloadLabel":max-count="1":template-download="true":is-show-file-list="false":download-url="'模板下载链接'":after-read="afterRead"/><!-- 上传至oss返回 --><div style="line-height:35px;">上传pdf文件至oss:</div><upload-file:file-type="'.pdf'":file-type-list="['application/pdf']"v-model="fileListPdf":is-put-oss="true":max-count="1"@input="uploadSuccess"/><div style="line-height:35px;">上传图片文件至oss:</div><upload-img v-model="fileList" :is-put-oss="true" :max-count="1" :oss-path-prefix="'yzsurvey_file/PC'" @input="uploadSuccess"/></div></template><script>import uploadFile from '@/views/components/uploadFile/index'import uploadImg from '@/views/components/uploadImg/index'export default {// 名字name: 'UploadFile',// 部件components: {uploadFile,uploadImg},// 静态props: {},// 数据data() {return {// word文件fileListFile: [],// word文件文本downloadLabel: '导入至本地文件及模板下载',// Pdf文件fileListPdf: [],// 图片文件文件fileList: []}},// 属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。主要当作属性来使用;computed: {},// 对象内部的属性监听,也叫深度监听watch: {},// 请求数据created() {},mounted() {},// 方法表示一个具体的操作,主要书写业务逻辑;methods: {/*** @description:文件上传成功返回* @param {*}* @return {*}* @param {*} file 本地文件信息*/afterRead(file) {console.log('file-----', file)},/*** @description:图片/文件上传线上成功返回* @param {*}* @return {*}* @param {*} data 文件信息及线上链接*/uploadSuccess(data) {console.log('imageData-----', data)}}}</script><style scoped lang="scss">.community-administrative{padding:10px 10px 0 10px;}</style>

效果展示如下:

文见传入格式点此处查看

使用以上组件

存在问题:上传会存在上下闪动问题

出现原因:fileList双向绑定会导致value赋值两次

点此处进行办法解决>>

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。