上传文件时如何获取视频长度

上传时如何获取视频长度

思路

首先,获取视频长度,我们通过浏览器自带的API就可以获取,如下:

<!-- html -->
<video src="xxx.mp4"></video>

<!-- JS -->
const oVideo = document.getElementById('video');

oVideo.duration; // 此属性就可以拿到时间

此时可以发现,src中只能填入路径,但是上传文件我们只有文件,所以问题就集中在如何拿到路径

  • 思路一:通常预览图片的时候,我们会将文件转成base64 然后赋值给 src,那么此时视频是不是可以同理呢..

  • 思路二:URL.createObjectURL()

此方法能创建一个临时的URL,和此窗口的生命周期绑定,刚好符合

MDN解释

静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。

注意URL.revokeObjectURL() 方法来释放

  • 思路三:既然拿到了file 对象,能不能直接从中获取信息

从file对象的属性中来看 没有此类信息, 那么如何来解决呢?

分析mp4文件,总所周知,计算机上所有的文件都是以2进制的形式存在,各种不同类型的文件都是有不同的规则来组织起来的,本质都是0和1,那么以mp4为例,在文件的二进制中,肯定有对于时长的描述,借助JS中操作二进制数据的能力处理Uint8Array、DataView、ArrayBuffer

那么主要问题就是知道文件规范...

代码实现

  • 方法一 (文件过大就会慢)
function change (e) {
      const file = oFile.files[0];
      let reader = new FileReader();

      const oVideo = document.createElement('video');

      const aBlob = new Blob([file],{type:'video/mp4'})
      reader.onload = function(result) {
        // console.log(reader.result);
        oVideo.src = reader.result
      }
      reader.readAsDataURL(aBlob);
      // oVideo.preload = 'metadata';
      oVideo.onloadedmetadata = function (e) {
        // 视频总长度,秒为单位
        console.log('....', oVideo.duration);
      }
      // document.body.appendChild(oVideo);
    }
  • 方法二
const file = oFile.files[0];
const oVideo = document.createElement('video');

// oVideo.preload = 'metadata';
oVideo.onloadedmetadata = function (e) {
  // 视频总长度,秒为单位
  console.log( '....', oVideo.duration);
}
oVideo.src = URL.createObjectURL(file)

注意:preload="metadata" > 提示尽管作者认为用户不需要查看该视频,不过抓取元数据(比如:长度)还是很合理的。

上述两种方式都是借用Video元素提供的API和onloadedmetadata事件实现

  • 方法三
function change2 () {
       const file = oFile.files[0];
      console.log(file)

      file.arrayBuffer().then(res => {
        console.log(res)

        const ui8 = new Uint8Array(res);
        const view = new DataView(res)
        console.log(ui8)
        console.log(view)
        analysis(view);
      })
    }


    function analysis (view) {
      // 下标
      let idx = 0;

      for(; idx < view.byteLength ;) {

        boxHeader = analysisHeaderBox(view, idx);
        console.log(boxHeader, '...')

        // 找到moov 处理
        if (boxHeader.type === 'moov') {
          // 跳过头
          const son = view.buffer.slice(idx + 8, view.byteLength);
          analysis(new DataView(son));
        }

        if (boxHeader.type === 'mvhd') {
          // 跳过头
          const son = view.buffer.slice(idx + 8, boxHeader.size);
          analysisMvhdBox(new DataView(son));
        }

        // 偏移
        idx += boxHeader.size;
      }
    }

    // box header 中的 size 和 type
    function analysisHeaderBox (view, offset) {
      const size  = view.getUint32(offset);

      offset += 4;
      let type = analysisBoxType(
        view.getUint8(offset),
        view.getUint8(offset + 1),
        view.getUint8(offset + 2),
        view.getUint8(offset + 3)
      );
      return {
        size,
        type
      };
    }

    // 根据码点获取字符串
    function analysisBoxType () {
      let str = '';
      for(var i = 0; i < arguments.length; i++) {
        str += String.fromCharCode(arguments[i]);
      }
      return str;
    } 

    function analysisMvhdBox (view) {
      console.log(view, '...')

      const timescale = view.getUint32(12);
      const duration = view.getUint32(16);

      console.log(timescale, duration, duration/timescale, '...')
    }


扩展

通过第三种方式,有以下好处

  • 可解析不同的文件类型,可以获取文件内容中的信息

  • 并且不依赖于 BOM环境 即使在node环境下也是能使用。

  • 就算没有后缀或者后缀错误也可以判断出来文件是不是MAP4或其他类型

参考

参考:
https://blog.csdn.net/w5025/article/details/123097108

http://www.360doc.com/content/20/0902/20/49586_933661079.shtml

https://zhuanlan.zhihu.com/p/457888765

posted @ 2022-05-11 09:50  古月大叔  阅读(988)  评论(0编辑  收藏  举报