赞
踩
说明:
请求用了自定义封装的原生js的ajax请求和axios
视频播放用了vue-video-player插件
文件的md5编码用了sparkmd5.js
读取二进制文件用的客户端自带的FileReader接口
封装的分片上传和断点续传组件:
<template> <div class="biguploadfile"> <div class="videoBox"> <div class="progressBox" v-if="showProgress"> <span class="progressItem" ref="progressItem"></span> </div> <videoPlay :src="fileBaseUrl+src" poster='' v-if="showVideo && !pauseStatus && !showProgress"></videoPlay> </div> <input type="file" class="bigFile" @change="computedSliceMd5" ref="file" name="file"> <div class="btns"> <Button type="primary" v-if="!pauseStatus" @click="toUpload">上传</Button> <Button type="default" v-if="pauseStatus" @click="pauseUpload">取消</Button> </div> </div> </template> <script> import SparkMD5 from "spark-md5"; //获取二进制文件md5编码 import videoPlay from "@/components/videoPlayer"; //利用vue-video-player插件封装的播放器 import Ajax from "@/plugins/ajax.js"; //封装的原生JS ajax请求 import api from "@/api/commonApi.js"; //本地用的接口 import code from "@/config/base.js"; //这是项目中自定义的参数 export default { data() { return { showVideo: false, blobSlice: null, file: null, identifier: null, chunkSize: 1024000, chunks: 0, currentChunk: 0, spark: null, fileReader: null, tmpDataList: [], formDataList: [], uploadedList: [], start: 0, end: 0, headers: {}, urlCode: code.urlCode.lectureDemand, xhr: null, pauseStatus: false, showProgress: false, percent: 0 }; }, props: { src: { //利用props将上传的视频地址传回来,用于预览 type: String, default: "" } }, components: { videoPlay }, //播放器组件 methods: { pauseUpload() { //取消上传 this.xhr.abort(); this.xhr = null; }, toUpload() { //调用上传接口 this.$refs.file.click(); }, checkMd5() { //断点续传需要用的方法,断点续传获取到的是已经上传过去的分片编号,已经上传过的分片直接跳过去 this.showProgress = true; this.$axios .post(api.lecturedemanduploadfilemd5, { md5: this.identifier }) .then(resp => { if (resp.data.object.code == 2) { let tmpIdsList = resp.data.object.ids; tmpIdsList.map((el,index) => { tmpIdsList[index] = parseInt(el); }); this.uploadedList = tmpIdsList; } this.showVideo = false; this.pauseStatus = true; this.uploadFile( 1, this.formDataList, this.uploadedList, this.chunks ); }) .catch(err => { this.initParam(0, "断点续传检查失败,请重试"); }); }, computedSliceMd5() { //获取文件的md5编码 this.file = this.$refs.file.files[0]; this.chunks = Math.ceil(this.file.size / this.chunkSize); this.fileReader.onload = e => { this.spark.append(e.target.result); // Append array buffer this.currentChunk++; if (this.currentChunk < this.chunks) { this.loadNext(); } else { this.identifier = this.spark.end(); //文件的MD5身份标识 this.tmpDataList.map((el, i) => { let formData = new FormData(); formData.append("filename", this.file.name); formData.append("relativePath", this.file.name); formData.append("totalSize", this.file.size); formData.append("chunkSize", this.chunkSize); formData.append("identifier", this.identifier); formData.append("totalChunks", this.chunks); formData.append("file", el.file); formData.append("currentChunkSize", el.currentSize); formData.append("chunkNumber", el.currentNum + 1); formData.append("urlCode", this.urlCode); this.formDataList.push(formData); }); this.checkMd5(); } }; this.fileReader.onerror = function() { this.$Message.error("读取文件出错,请重试"); }; this.loadNext(); }, loadNext() { //文件切片 (this.start = this.currentChunk * this.chunkSize), (this.end = this.start + this.chunkSize >= this.file.size ? this.file.size : this.start + this.chunkSize); let pieceFile = this.blobSlice.call( this.file, this.start, this.end ); pieceFile.name = this.file.name; let tmpObj = { file: pieceFile, currentSize: this.end - this.start, currentNum: this.currentChunk }; this.tmpDataList.push(tmpObj); this.fileReader.readAsArrayBuffer(pieceFile); }, uploadFile(n, dataList, uploadedList, chunks) { //利用promise异步变同步上传 console.log("uploadedList",uploadedList); let status = uploadedList.indexOf(n) == -1 ? true : false; console.log("status",status); let key = n - 1; if (status) { new Promise((resolve, reject) => { Ajax( resolve, reject, "post", api.lecturedemanduploadfile, this.headers, dataList[key], this.xhrReturn, this.getProgress ); }) .then(resp => { if(resp.code == "401"){ this.$router.push({path:"/login"}); return ; } if (resp.object.uploadStatus == 1) { let tmpUrl = JSON.parse(resp.object.fileUrl)[0] .fileName; this.$emit("getUrl", tmpUrl); this.success(); } else { this.uploadFile( n + 1, this.formDataList, this.uploadedList, this.chunks ); } }) .catch(err => { this.initParam(0, "上传失败"); }); }else{ this.percent += Math.floor(100/this.chunks); this.changeProgress(1); this.uploadFile( n + 1, this.formDataList, this.uploadedList, this.chunks ); } }, getProgress(evt) { //ajax进度条回调方法 if (evt.lengthComputable) { this.percent += Math.floor( (evt.loaded * 100) / this.chunks / evt.total ); if(this.percent>=99){ this.percent = 99; } this.changeProgress(1); } else { this.changeProgress(0); } }, changeProgress(type) { //进度条展示 if (type) { this.$refs.progressItem.innerHTML = this.percent.toFixed(2) + "%"; this.$refs.progressItem.style.width = this.percent.toFixed(2) + "%"; } else { this.$refs.progressItem.innerHTML = "上传失败"; } }, success() { //成功回调 this.percent = 100; this.changeProgress(1); setTimeout(() => { this.initParam(1, "上传成功"); }, 500); }, failed(err) { //失败回调 this.initParam(0, err); }, initParam(type, msg) { //成功或失败之后的初始化 this.percent = 0; this.pauseStatus = false; this.showProgress = false; this.file = null; this.identifier = null; this.chunks = 0; this.currentChunk = 0; this.tmpDataList = []; this.formDataList = []; this.uploadedList = []; this.start = 0; this.end = 0; this.$refs.file.value = ""; if (type == 1) { this.showVideo = true; this.$Message.success(msg); } else { this.showVideo = false; this.$Message.error(msg); } }, xhrReturn(xhr) { this.xhr = xhr; } }, created() { //一些变量的初始化 if (Boolean(this.src)) { this.showVideo = true; } this.blobSlice = window.File.prototype.slice || window.File.prototype.mozSlice || window.File.prototype.webkitSlice; this.spark = new SparkMD5.ArrayBuffer(); this.fileReader = new FileReader(); this.percent = 0; } }; </script> <style lang="less" scoped> .biguploadfile { display: inline-block; .bigFile { display: none; } .progressBox { display: inline-block; width: 200px; height: 20px; background-color: gray; vertical-align: middle; } .progressItem { display: inline-block; height: 20px; background-color: #2d8cf0; font-weight: bold; color:#ffffff; } .btns { display: inline-block; vertical-align: middle; } .videoBox { display: inline-block; vertical-align: middle; } } </style>
可参考的自定义封装的Ajax
function getXhr(){ let xhr=null; if(window.XMLHttpRequest){ xhr = new XMLHttpRequest(); } else { //为了兼容IE6 xhr = new ActiveXObject('Microsoft.XMLHTTP'); } return xhr; } export default function Ajax(resolve,reject,type, url, headers, data, xhrReturn=()=>{}, progress=()=>{}){ //参数可封装成对象形式的,用着方便,这里就不改了 /* ajax暂停之后不能重新发送请求,需要创建新的xhr对象重新发送请求 */ let xhr = getXhr(); xhrReturn(xhr);//将xhr对象返回,用于取消上传 type = type.toUpperCase(); let reg = new RegExp('\\?',"g"); if(reg.test(url)){ reject("url地址传递错误"); return ; } let random = Math.random(); url += "?"+random; if(type == 'GET'){ reject("不支持GET提交方式"); } else if(type == 'POST'){ xhr.open('POST', url, true); if(typeof headers == 'object'){ for(let key in headers){ // 如果需要像 html 表单那样 POST 数据,请使用 setRequestHeader() 来添加 http 头。 xhr.setRequestHeader(key, headers[key]); } }else{ reject("请求头设置有误"); return ; } xhr.withCredentials = false; xhr.upload.onprogress = progress; xhr.onabort = ()=>{ xhr = null; }; xhr.send(data); } // 处理返回数据 xhr.onreadystatechange = function(){ if(xhr.readyState == 4){ if(xhr.status == 200){ let resp = JSON.parse(xhr.responseText); resolve(resp); } else { reject("提交失败,请重试"); } } } }
可参考的videoPlayer组件
<template> <div class="container"> <div class="video-box" :style="videoStyle"> <video-player class="video-player vjs-custom-skin video-viewport" ref="videoPlayer" :playsinline="true" :options="playerOptions" @play="onPlayerPlay($event)" @pause="onPlayerPause($event)" ></video-player> </div> </div> </template> <script> import cookie from "./../utils/cookie.js"; import api from "@/api/commonApi"; export default { data() { return { playerOptions: { playbackRates: [0.5, 1.0, 1.5, 2.0, 3.0, 6.0, 12.0], //播放速度 autoplay: false, //如果true,浏览器准备好时开始回放。 muted: false, // 默认情况下将会消除任何音频。 loop: false, // 导致视频一结束就重新开始。 preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持) language: "zh-CN", aspectRatio: "4:3", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3") fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。 sources: [ { type: "video/mp4", src: "" //你的视频地址(必填) } ], poster: "", //你的封面地址 width: document.documentElement.clientWidth, notSupportedMessage: "此视频暂无法播放,请稍后再试", //允许覆盖Video.js无法播放媒体源时显示的默认信息。 controlBar: { timeDivider: true, durationDisplay: true, remainingTimeDisplay: false, fullscreenToggle: true //全屏按钮 } } }; }, props: { src: { type:String, default: "" }, poster: { type:String, default: "./static/img/heicon.jpg" }, videoStyle:{ type:Object, default:()=>{ return { width: "400px", height: "300px" }; } } }, methods: { onPlayerPlay(player) { console.log("触发播放"); }, onPlayerPause(player) { console.log("触发暂停"); } }, computed: { player() {//暂时没用到 return this.$refs.videoPlayer.player; } }, mounted() { this.playerOptions.sources[0].src = this.src; this.playerOptions.poster = this.poster; } }; </script> <style lang="less" scoped> .container { display:inline-block; .video-box { .video-viewport { width: 100%; height: 100%; } } } </style>
调用:
<template> <div> <!-- 用props将上传的视频地址传递过去,还有一个方法 --> <bigUploadFile :src="src" @getUrl="getUploadUrl"></bigUploadFile> </div> </template> <script> import bigUploadFile from "@/components/bigUploadFile"; export default { data() { return { src: "" }; }, created() {}, components: { bigUploadFile }, methods: { //获取上传的url getUploadUrl(url) { console.log("传递过来的url", url); this.src = url; } } }; </script> <style lang="less"> </style>
作者:喜大普奔⁶⁶⁶
来源:CSDN
原文:https://blog.csdn.net/qq_38652871/article/details/88229749
版权声明:本文为博主原创文章,转载请附上博文链接!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。