前言
开发过程中我们难免会遇到上传视频的需求。如果视频过大或者后端需要将前端上传的视频切割为播放更友好的m8u3格式,我们的分段上传视频就显得尤为重要
注意事项
下面的代码基于vue2.0框架使用混入的方法进行调用。需要注意的是如果你的项目使用了ESlint需要关闭while (true) 循环条件永恒进入的校验 or 用自己方法编写也是可以的
方案一:纯js写法(推荐)
封装对象
因后端处理方式统一 前端市面上没有太多的视频分片插件 此作品给大家留出了绝对的自用空间 适配大多数需求。 需要大家根据自己需求请求一下接口就好了。
const upVideo={
file:null,//文件对象
size:0,//分片大小 kb
progressBar:0,//进度条 num%
underWayFn:null,//进度条改变触发函数
upOverFn:null,//上传完成触发函数
t:null,//定时器
// 初始化函数
init({
fileObj,//文件对象
size=2,//分片大小 默认2 单位m
underWayFn=function(){},//进度条改变触发函数
upOverFn=function(){},//上传完成触发函数
}){
this.file=fileObj;
this.size=size*1024*1024;
this.underWayFn=underWayFn;
this.upOverFn=upOverFn;
// 判断是否是满足条件的视频对象 满足条件调用this.upbegin()处理
this.upbegin();
},
// 上传文件
async upbegin(){
let videoTime = await this.videoLong();//视频播放时间
// 视频分片
let start = 0, end = 0, chunkArr= [], size = this.size;
let file=this.file;
function chuli(){
end += size;
var blob = file.slice(start, end);
start += size;
if (blob.size) {
chunkArr.push(blob);
chuli(file);
}else{
return chunkArr
}
}
chuli();
//预请求接口 然后this.inTurnto(chunkArr); 分片请求
// 分片请求主体
this.inTurnto(chunkArr);
console.log(videoTime,this.file)
},
// 缓慢增长到目标进度条
changeProgressBar(num){
clearInterval(this.t)
this.t = setInterval(() => {
if(this.progressBar == num){
clearInterval(this.t)
//上传完成
if(this.progressBar === 100){
this.upOverFn();//通知上传完成
this.clearUpVideo();//格式化重置
}
}else{
this.progressBar++;
this.underWayFn(this.progressBar);//改变进度条通知
}
}, 50);
},
// 多个视频一一请求
inTurnto(chunkArr){
const chunkAllNum=chunkArr.length;//片段总数量
let count=0;//完成个数
chunkArr.forEach( (item,index) =>{
// 模拟数据请求
setTimeout(() => {
console.log(item,index)
count++;//增加当前进度
this.changeProgressBar( parseInt(count/chunkAllNum*100) );//改变进度
}, 1000);
})
},
// 获取视频总时长
videoLong(){
return new Promise((resolve)=>{
var url = URL.createObjectURL(this.file);
var audioElement = new Audio(url);
audioElement.addEventListener("loadedmetadata", function() {
var hour = parseInt((audioElement.duration) / 3600);
if (hour<10) { hour = "0" + hour; }
var minute = parseInt((audioElement.duration % 3600) / 60);
if (minute<10) { minute = "0" + minute; }
var second = Math.ceil(audioElement.duration % 60);
if (second<10) { second = "0" + second; }
resolve(hour + ":" + minute + ":" + second)
});
})
},
// 重置
clearUpVideo(){
this.file=null;
this.size=0;
this.progressBar=0;
this.underWayFn=null;
this.upOverFn=null;
this.t=null;
},
}
export default upVideo
上文中需要自己根据需求调接口的地方
init 中条件判断是否是满足条件的视频对象进行处理
upbegin 中预请求接口
inTurnto中模拟数据的请求
使用示例
这里没什么好说的 按照规则写就行
upVideo.init({
fileObj:file,//视频文件 必传
size:3,//分片大小 选填 默认2单位M
underWayFn(num){//当前上传进度监听回调 选填
console.log('进度',num)
},
upOverFn(){//上传完成生命周期 选填
console.log('上传完成')
},
})
方案二:组件写法(不太推荐)
组件代码
/* 使用示例
使用时更改自己的接口地址 使用到依赖axios
import upviedo from './upvideo.js'
mixins:[upviedo],
methods: {
preview(file) {
this.num++
if(this.num>=2)return
this.uploadViedo(上传文件对象,分片允许最大M(Num)).then(res=>{
console.log('总体完成')
}).catch(err=>{
console.log('总体失败')
})
},
}
this.Loading 变量当前上传进度
*/
export default {
data() {
return {
NowTotal:0,//当前
AllTotal:1,//总数
Loading:0,//当前进度%
clearT:0,//清除定时器
};
},
mounted(){
// 检测断网
window.addEventListener("offline", () => {
console.log("已断网");
});
window.addEventListener("online", () => {
console.log("网络已连接");
});
},
methods: {
// 上传进度
changeLoading(){
this.NowTotal++;
let ratio = (this.NowTotal * 100) / (this.AllTotal * 100)
ratio = Math.round(ratio * 100)
clearInterval(this.clearT)
this.clearT = setInterval(()=>{
this.Loading++;
if(this.Loading>=ratio)clearInterval(this.clearT);
},10)
},
// 视频长度
videoLong(file){
return new Promise((resolve,reject)=>{
var url = URL.createObjectURL(file);
var audioElement = new Audio(url);
audioElement.addEventListener("loadedmetadata", function(_event) {
var hour = parseInt((audioElement.duration) / 3600);
if (hour<10) { hour = "0" + hour; }
var minute = parseInt((audioElement.duration % 3600) / 60);
if (minute<10) { minute = "0" + minute; }
var second = Math.ceil(audioElement.duration % 60);
if (second<10) { second = "0" + second; }
resolve(hour + ":" + minute + ":" + second)
});
})
},
// file :文件 byte :每片字节大小 单位M
async uploadViedo(file,byte = 2){
var duration =await this.videoLong(file);
let chunkSize = byte * 1024 * 1024 //分片大小
let chunkArr = [];
if (file.size < chunkSize) {
chunkArr.push(file.slice(0))
} else {
var start = 0, end = 0
while (true) {
end += chunkSize
var blob = file.slice(start, end)
start += chunkSize
if (!blob.size) {
break;
}
chunkArr.push(blob)
}
}
this.AllTotal = chunkArr.length;
return new Promise((ok,no)=>{
//预链接
this.$axios.post('https:',{
"video_number":this.AllTotal,//总共分为几片
"size":file.size,//文件总大小
"video_len": duration || 0,//视频长度
"suffix":file.type,//文件类型
}).then(res=>{
let all=[];
chunkArr.forEach((item,index)=>{
let p = new Promise((resolve,reject)=>{
var data = new FormData();
data.append('video', item)//视频主体
data.append('name', res.data.data)//视频名称
data.append('blob_num', index+1)//当前为第几段视频
data.append('total_blob_num', this.AllTotal)//总分为几段
//上传主体
this.$axios.post('https:',data).then(res=>{
this.changeLoading();
resolve(res)
},err=>{
reject(err)
})
})
all.push(p);
})
Promise.all(all).then(res=>{
ok()
}).catch(err=>{
no(err)
})
}).catch(err=>{
no(err)
})
})
}
},
beforeDestroy(){
clearInterval(this.clearT)
}
}
使用示例
这里仅用element组件 编写示例 方便理解cv到自己项目
<template>
<div class="hello">
<el-upload
class="upload-demo"
drag
action="https://jsonplaceholder.typicode.com/posts/"
:on-change="preview"
:multiple="false">
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
</div>
</template>
<script>
import upviedo from './upvideo.js'
export default {
mixins:[upviedo],
props: {
msg: String
},
created(){
},
data() {
return {
num:0,
};
},
computed:{},
mounted(){
},
methods: {
preview(file) {
this.num++
if(this.num>=2)return
console.log(file.raw)
//上面代码都可以无视 这里是最主要的
//第一个参数 :视频文件体 第二个参数:Num 每片视频限制最大字节(单位M) 不传默认2M
this.uploadViedo(file.raw,2).then(res=>{
console.log('总体完成')
}).catch(err=>{
console.log('总体失败')
})
},
},
}
</script>
<style scoped>
</style>
总结
本文仅提供思路 根据自己的需求灵活修改 文中不对的地方欢迎大佬指正,使用中出现问题欢迎评论或私信
收藏方便下次使用 开源不易,绝对原创 点个赞吧
转自小吕博客,没错我就是小吕!大佬请别喷