隐藏
换装

Vue / 前端 · 2022年5月10日 2

Vue+ElementUi阿里Oss断点续传组件

昨天后端跑过来说阿里oss视频上传要前端自己集成直传,话不多说直接上代码

这里使用的是STSToken方式上传的,获取STS的方法需要后端去集成出来一个接口,相关文档链接在下方

相关文档点击跳转:

使用临时凭证访问STSToken访问OSS

各语言获取STSToken的方式STS SDK支持Java、.NET、Python、PHP、Node.js和Go语言

需要注意的是init方法内需要换成自己后端给的接口拿到配置信息,STStoken方式上传使用的是后端获取的临时accessKeyId跟临时secret,而不是阿里云账户固定的keyid

后端返回接口格式如下:

<template>
	<!--在此处添加渲染的内容-->
	<div>
		<el-upload class="upload-demo" ref="upload" :on-success="handleSuccess" :before-upload="beforeUpload"
			accept="video/*" :http-request="handleHttpRequest" :headers="uploadHeaders" :limit="1" :file-list="fileList"
			:show-file-list="false" action="">
			<el-button slot="trigger" size="small" type="primary" icon="el-icon-plus"
				:disabled="uploadType == 'loading' || uploadType == 'pause'" style="margin-right: 10px;">选取文件
			</el-button>
			<el-button size="small" @click="pauseUpload" type="warning" icon="el-icon-video-pause"
				v-if="uploadType == 'loading'">暂停上传
			</el-button>
			<el-button size="small" @click="resumeUpload" type="warning" icon="el-icon-refresh"
				v-if="uploadType == 'pause' || uploadType == 'error'">恢复上传
			</el-button>
			<el-button size="small" @click="clearFile" type="danger" icon="el-icon-delete"
				v-if="uploadType == 'error' || uploadType == 'success'">清除文件</el-button>
			<div slot="tip" v-if="file != null" class="el-upload-list__item-preview">{{file.name}}</div>
			<div slot="tip" class="el-upload__tip">只能上传视频文件 文件大小不超过10G</div>
			<el-progress v-if="percent > 0" :text-inside="true" :stroke-width="18" :percentage="percent"
				:status="percentType" style="margin-top: 10px;"></el-progress>
		</el-upload>
	</div>
</template>

<script type="text/ecmascript-6">
	import request from "@/service/http"
	import proxy from '@/utils/proxy'
	import qs from 'qs'
	import OSS from 'ali-oss'
	//将渲染的内容导出
	export default {
		props: {
			prefix:{
				type:String,
				default () {
					return '';
				},
			}
		},
		data() {
			return {
				region: 'oss-cn-beijing',
				bucket: '', //这里选择OSS容器
				percent: 0,
				url: '', //后台获取token地址
				ClientObj: null,
				dataObj: {},
				expiration: '',
				fileList: [],
				uploadHeaders: {
					authorization: '*'
				},
				disabled: false,
				client: null,
				isInit: false,
				uploadType: 'wait', // success 成功 error 失败 loading 上传中 pause 暂停上传 wait 未操作
				percentType: 'primary', // 进度条状态
				abortCheckpoint: null, // 断点
				fileSuccessUrl: [] //最终上传的地址
			}
		},
		watch: {
			fileSuccessUrl() {
				if (this.fileSuccessUrl.length > 0) {
					if (this.fileSuccessUrl.length == 1) {
						//拆分掉返回的uploadid
						this.$emit("input", this.fileSuccessUrl[0].substring(0, this.fileSuccessUrl[0].lastIndexOf("?")));
					} else {
						//拆分掉返回的uploadid
						this.fileSuccessUrl.forEach((item,i)=>{
							this.fileSuccessUrl[i] = this.fileSuccessUrl[i].substring(0, this.fileSuccessUrl[i].lastIndexOf("?"));
						})
						this.$emit("input", this.fileSuccessUrl);
					}
					this.$emit('success',this.fileSuccessUrl);
				}
			},
		},
		methods: {
			/**
			 * 请求接口拿到accesskeyid 等等一些配置信息 并初始化阿里oss 
			 * @author 木头人下士
			 * @time 2022-05-09
			 */
			async init() { //获取Token
				this.fileList = [];
				this.file = null;
				this.uploadType = 'wait'; // success 成功 error 失败 loading 上传中 pause 暂停上传 wait 未操作
				this.percentType = 'primary'; // 进度条状态
				this.abortCheckpoint = null;
				let param = {
					userId: this.$store.state.userId
				};
				
				//获取sts token拿到参数配置
				let res = await request({
					url: proxy.baseURL + `/api/ali/getAliStsToken`,
					method: 'post',
					data: qs.stringify(param)
				})
				if (res.code == 0) {
					this.dataObj = {
						bucket: 'hoohui',
						region: 'oss-cn-beijing',
						accessKeyId: res.data.credentials.accessKeyId, //临时accessid
						accessKeySecret: res.data.credentials.accessKeySecret, //临时秘钥
						stsToken: res.data.credentials.securityToken,
					};
					//初始化
					this.client = new OSS(this.dataObj),
						this.isInit = true; //初始化完毕
					this.$forceUpdate();
				} else {
					this.$message.error('初始化失败')
				}
			},
			/**
			 * 上传文件前 文件验证大小 并检测是否初始化阿里oss 如未初始化则先初始化 
			 * @author 木头人下士
			 * @time 2022-05-09
			 */
			async beforeUpload(file) {
				
				//限制的上限为10G  
				const maxSize = 1024 * 1024 * 1024 * 10;
				if (maxSize < file.size) {
					this.$message.error('上传文件大小不能超过 10G!');
					return false;
				}
				
				if (!this.isInit) { //判断有没有初始化
					await this.init(); //初始化流程
				}
			},
			/**
			 * 上传到oss 
			 * @author 木头人下士
			 * @time 2022-05-09
			 */
			async handleHttpRequest(option) { //上传OSS
				try {
					let that = this;
					that.uploadType = 'loading';
					that.percentType = 'warning';
					let file = option.file;
					that.file = file;
					that.$emit("chooseFile", file);
					//随机命名
					
					const random_name = that.random_string(6) + '_' + new Date().getTime() + '.' + file.name.split('.')
						.pop();
					// 分片上传文件
					await that.client.multipartUpload(that.prefix + random_name, file, {
						progress: async function(p, cpt, res) {
							
							// 为中断点赋值。
							that.abortCheckpoint = cpt;
							
							that.percent = parseInt(p * 100);
							
						}
					}).then(({
						res
					}) => {
						
						if (res.statusCode === 200) {
							// option.onSuccess(ret)
							that.fileSuccessUrl = res.requestUrls;

							that.uploadType = 'success';
							that.percentType = 'success';
						} else {

							that.uploadType = 'error';
							that.percentType = 'error';
							
						}
					}).catch(error => {
						that.uploadType = 'error';
						that.percentType = 'error';
						
						
					});

				} catch (error) {

					this.uploadType = 'error';
					this.percentType = 'error';
					
					
				}
			},
			/**
			 * 上传成功 
			 * @author 木头人下士
			 * @time 2022-05-09
			 */
			handleSuccess(response, file, fileList) {
				this.uploadType = 'success';
				this.percentType = 'success';
				
				
				
			},
			/**
			 * 暂停上传 
			 * @author 木头人下士
			 * @time 2022-05-09
			 */
			pauseUpload() {
				this.client.cancel();
				this.uploadType = 'pause';
				this.percentType = 'warning';
			},
			/**
			 * 清除文件 
			 * @author 木头人下士
			 * @time 2022-05-09
			 */
			clearFile() {
				this.fileList = [];
				this.file = null;
				this.uploadType = 'wait';
				this.percentType = 'primary';
				this.percent = 0;
			},
			/**
			 * 恢复上传 
			 * @author 木头人下士
			 * @time 2022-05-09
			 */
			async resumeUpload() {
				let that = this;
				that.uploadType = 'loading';
				that.percentType = 'warning';
				let file = that.file;
				let abortCheckpoint = that.abortCheckpoint;
				await that.client.multipartUpload(name, file, {
					checkpoint: abortCheckpoint,
					progress: (p, cpt, res) => {
						// 为了实现断点上传,您可以在上传过程中保存断点信息(checkpoint)。发生上传错误后,将已保存的checkpoint作为参数传递给multipartUpload,此时将从上次上传失败的地方继续上传。
						// 为中断点赋值。
						that.abortCheckpoint = cpt;
						
						// 获取上传进度。
						
						that.percent = parseInt(p * 100);
						
					},
				}).then(({
					res
				}) => {
					
					if (res.statusCode === 200) {

						that.fileSuccessUrl = res.requestUrls;
						that.uploadType = 'success';
						that.percentType = 'success';
					} else {
						that.uploadType = 'error';
						that.percentType = 'error';
						
					}
				}).catch(error => {
					that.uploadType = 'error';
					that.percentType = 'error';
					
					
				});;
			},
			/**
			 * 设置进度条状态 
			 * 发现最后更新数据后页面没有达到预期效果 没有用了
			 * @author 木头人下士
			 * @time 2022-05-09
			 * @param {Object} type
			 */
			// async setPercentType(type) {
			// 	
			// 	this.$set(this, 'uploadType', type);
			// 	
			// 	switch (type) {
			// 		case 'wait' || 'loading':
			// 			this.$set(this, 'percentType', 'primary');
			// 			this.percentType = 'primary';
			// 			break;
			// 		case 'pause':
			// 			this.$set(this, 'percentType', 'warning');
			// 			this.percentType = 'warning';
			// 			break;
			// 		case 'success' || 'error':
			// 			if (type == 'success') {
			// 				this.percent = 0;
			// 			}
			// 			this.$set(this, 'percentType', type);
			// 			this.percentType = type;
			// 			break;
			// 		default:
			// 			this.uploadType = 'wait';
			// 			this.percentType = 'primary';
			// 			break;
			// 	}
			// 	await this.$forceUpdate();
			// },
			getDate() {
				const date = new Date(),
					year = date.getFullYear(),
					month = date.getMonth() > 9 ? date.getMonth() + 1 : `0${date.getMonth() + 1}`,
					day = date.getDate() > 9 ? date.getDate() : `0${date.getDate()}`,
					hh = date.getHours() > 9 ? date.getHours() : `0${date.getHours()}`,
					mm = date.getMinutes() > 9 ? date.getMinutes() : `0${date.getMinutes()}`;
				return `${year}${month}${day}${hh}${mm}`;
			},
			// 随机生成文件名
			random_string(len) {
				len = len || 32;
				let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz12345678',
					maxPos = chars.length,
					pwd = '';
				for (let i = 0; i < len; i++) {
					pwd += chars.charAt(Math.floor(Math.random() * maxPos));
				}
				return pwd;
			}

		},
		components: {},
		computed: {},
		created() {},
		mounted() {},

	}
</script>

<style scoped>
	/**渲染内容的样式**/
</style>

使用方法

1:页面中引入组件

import uploadFile from "@/components/uploadFile";

2:注册到页面components,当然也可以注册到全局组件这里就不介绍了

components:{
	uploadFile
},

3:html部分添加

<uploadFile ref="uploadFile" @success="successFile" @chooseFile="chooseFile" v-model="successurl"></uploadFile>

4:组件使用的双向绑定,可以直接绑定一个数据就可以了

5:支持两个事件

methods:{
    //@chooseFile 监听选择文件,会把选中的文件返回
    chooseFile(file){console.log(file)},
    //@success 监听上传成功,会返回上传文件路径,这个可能是个数组,具体不清楚,oss官方文档给的是个数组,组件拿到返回值如果是数组会返回数组,如果数组为一项会直接返回文件地址字符串
    successFile(urls){console.log(urls)}
}