video 切片上传探索

参考文章:

1.https://blog.csdn.net/zrcj0706/article/details/103137617(写的挺好的、就是有几个外部引入的方法没有写全)

最后打算用这个方法,三个外部引入的方法都是关于接口请求的, 整体感觉代码是较为核心的代码,但是像续传部分的代码逻辑不够好,他是直接同步调用接口的,不太好,而且还有他的MD5计算不够精准
计算md5文件的两种方法

2、https://blog.csdn.net/qq_38652871/article/details/88229749

这份代码算是比较全的了,他说自己在项目中试过了,可以参考,也是没有用组件的, 切片上传和断点续传都写好了,里面写的方法也挺好,上传进度条显示,断点续传,视频播放器组件,原生JS的AJAX封装,Promise异步变同步

3.https://www.cnblogs.com/xiahj/p/vue-simple-uploader.html#%E5%85%B3%E4%BA%8Evue-simple-uploader

这个没有试通过,引入的组件是vue-simple-upload,他加上了很多自己的业务组件,感觉直接去看vue-simple-upload的切片上传会更好一点。

4. js操作Blob合并

自己的总结

感觉吧, 分片上传和断点续传传还挺简单的
流程:

  1. 获取视频文件,计算文件的md5作为文件标志,询问服务端(MD5可作为文件的唯一标志)
  2. 有了就算秒传,直接返回url,没有或者有部分上传,就上传片段
  3. 片段获取通过 file.slice(start, end) 切片,生成blob文件,然后上传
  4. 在上传完所有分片后,服务端返回字段needMerge
  5. 调用merge的接口,由服务端合并视频
demo

本地预览切片上传, 本地合并切片上传, 切出来的文件气死就是blob, 文件本身就是blob
可总结的点:

  1. URL.createObjectURL(file/blob)
  2. file.slice(start, end) //start, end都是 size
  3. let fileRederInstance = new FileReader()
    fileRederInstance.readAsBinaryString(file)
    fileRederInstance.addEventListener(‘load’, e => {
    }) // read 文件,addload
  4. fileMD5 = md5(fileBolb)
  5. new Blob([blob1,blob2,blob3],{type:“image/png”});
<template>
  <div>
    <el-upload
        action
        :accept="'video/*'"
        :before-upload="beforeAvatarUpload"
      >
      <el-button>上传视频</el-button>
    </el-upload>
    <el-button @click="mergeChunks">合并chunks</el-button>
    
    <div v-for="(item, index) in videoUrl"  :key="index" >
      <video controls style="height: 300px; margin-top: 24px" :src="item"></video>
    </div>
  </div>
</template>

<script>
      /* // 核心方法就三个 
      fileRederInstance.readAsBinaryString(file)
        fileRederInstance.addEventListener('load', e => {
      }) */
      /* file.slice(start, end) */
      /* let fileBolb = e.target.result
        this.fileMD5 = md5(fileBolb) */
import md5 from 'js-md5'
import SparkMD5 from 'spark-md5' // 最初使用js-md5计算出来MD5值时不对的,应该和js-MD5没有关系,后来改成了sparkmd5
export default {
  data() {
    return {
      fileMD5: null,
      chunkSize: 100 * 1024,
      videoUrl: [],
      fileChunks: [],
    }
  },
  methods: {
  // 这儿计算出来的是整个文件的
  md5(file, chunkSize) {
      return new Promise((resolve, reject) => {
        let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
        let chunks = Math.ceil(file.size / chunkSize);
        let currentChunk = 0;
        let spark = new SparkMD5.ArrayBuffer();
        let fileReader = new FileReader();
      
        fileReader.onload = function(e) {
          console.log('e', e);
          spark.append(e.target.result); 
          currentChunk++;
          if (currentChunk < chunks) {
            loadNext();
          } else {
            let md5 = spark.end();
            console.log('md5', md5);
            this.fileMD5 = md5;
            resolve(md5);
          }
        };
      
        fileReader.onerror = function(e) {
          reject(e);
        };
      
        function loadNext() {
          let start = currentChunk * chunkSize;
          let end = start + chunkSize;
          if (end > file.size){
            end = file.size;
          }
          fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
        }
        loadNext();
      });
    },
    beforeAvatarUpload(file) {
      this.chunkCount = Math.ceil(file.size / this.chunkSize) // 总片数
      console.log('file', file);
      console.log('file.size / this.chunkSize', file.size,this.chunkSize);
      console.log('this.chunkCount', this.chunkCount);
      this.readFileMD5(file)
    },
    async readFileMD5(file) {
     await this.md5(file, 1024 * 1024 * 10)
     this.readChunkMD5(file);
      /* console.log('file', file);
      // this.videoUrl.push(URL.createObjectURL(file)) 
      let fileRederInstance = new FileReader()
      fileRederInstance.readAsBinaryString(file)
      fileRederInstance.addEventListener('load', e => {
        console.log('e', e);
        let fileBolb = e.target.result
        // 这儿计算出来的可能只是第一份chunk的md5; 所以这儿要改成spark版本的 
        this.fileMD5 = md5(fileBolb)
        console.log('fileMD5', this.fileMD5); // 这儿未知就获取到了文件的唯一标志, 即使文件名换了也不会改变
      }) */
    },
    readChunkMD5(file) {
      console.log('this.chunkCount', this.chunkCount);
      for (var i = 0; i < this.chunkCount; i++) {
        console.log('i', i);
        const { chunk } = this.getChunkInfo(file, i, this.chunkSize)
        //  应该用promise all 或者promise顺序执行?  不一定要用promise, 直接一起传, 速率为每个单个的相加(单个的速率的计算有在另外一篇文章中写, 可能不是特别完善)
        this.uploadChunk({ chunk, currentChunk: i, chunkCount: this.chunkCount })
      }
    },
    getChunkInfo(file, currentChunk, chunkSize) {
      let start = currentChunk * chunkSize
      let end = Math.min(file.size, start + chunkSize)
      let chunk = file.slice(start, end) //切割出来的还是一个blob,实际上file就是一个blob的实例
      console.log('chunk', chunk);
      return { start, end, chunk, currentChunk }
    },
    uploadChunk(chunkInfo){
      console.log('chunkInfo', chunkInfo);
      this.fileChunks.push(chunkInfo)

      // 试验: 是否所有的blob都可以播放呢?
      this.videoUrl.push(URL.createObjectURL(chunkInfo.chunk)) 
      // 1、URL.createObjectURL(chunkInfo.chunk)  是可以将blob转换成url的
      // 2、 只有第一个blob可以播放,播放时长为 1/chunkCount * 总时长

      /* let fetchForm = new FormData()
      fetchForm.append('chunk', chunkInfo.currentChunk)
      fetchForm.append('chunkSize', this.chunkSize)
      fetchForm.append('chunks', chunkInfo.chunkCount)
      fetchForm.append('file', chunkInfo.chunk)
      fetchForm.append('md5', this.fileMD5) */

    },
    // 合并blob, 也就是blob.slice的反操作, 合并后的文件可以被正常播放, 如果我们打乱呢? 还能被正常播放吗
    mergeChunks() {
      const Chunks = this.fileChunks.map(item => item.chunk)
      const mergedChunk = new Blob(Chunks,{type:"video/mp4"});
      [mergedChunk[0], mergedChunk[1]] = [mergedChunk[1], mergedChunk[0]] // 打乱blob位置, 好像是没有影响的哦
      this.videoUrl.push(URL.createObjectURL(mergedChunk)) 
    }
  }
}
</script>

<style>

</style>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值