【问题标题】:Get video duration when input a video file输入视频文件时获取视频时长
【发布时间】:2015-05-30 20:36:33
【问题描述】:

我正在做一个使用 HTML 和 Javascript 的项目,该项目将使用本地文件在本地运行。 我需要通过输入选择一个文件,获取其信息,然后决定是否将其添加到我的列表中并进行复制。如果我决定使用它,我将不得不将它放在队列中以备后用。否则我将丢弃并选择另一个文件。

我面临的问题是我无法找到一种方法来仅通过在输入上选择它来获取视频持续时间。

我进行了很多搜索,但没有找到任何获取持续时间的方法。在下面的这段代码中,我尝试使用 'file.duration' 但它不起作用,它只是返回 'undefined'。

这是我的输入,如您所见,正常。

<div id="input-upload-file" class="box-shadow">
   <span>upload! (ღ˘⌣˘ღ)</span> <!--ignore the text face lol -->
   <input type="file" class="upload" id="fileUp" name="fileUpload" onchange="setFileInfo()">
</div>

这是我用来获取所有信息的函数。

function setFileInfo(){
  showInfo(); //change div content
  var file = document.getElementById("fileUp").files[0];
  var pid =  1;
  var Pname = file.name;
  Pname = Pname.slice(0, Pname.indexOf(".")); //get filename without extension
  var Ptype = file.type;
  var Psize = bytesToSize(file.size); //turns into KB,MB, etc...
  var Pprior = setPriority(Ptype); //returns 1, 2 or 3 
  var Pdur = file.duration;
  var Pmem = getMemory(Psize); //returns size * (100 || 10 || 1)
  var Pown = 'user';
  /* a lot of stuff throwing this info to the HTML */
  console.log(Pdur);
}

有没有办法做到这一点?如果没有,有哪些替代方法可以帮助我?

【问题讨论】:

  • 您在寻找这样的东西吗? jsfiddle.net/derickbailey/s4P2v这不是我的,是音频的,但我无法想象视频会有很大的不同
  • @RobinHellemans 这个创建了一个
  • 知道如何使用 avi 文件进行这项工作吗?

标签: javascript html


【解决方案1】:

modern browsers 中,您可以使用 URL API 的 URL.createObjectURL() 和未附加的视频元素来加载文件的内容。

var myVideos = [];

window.URL = window.URL || window.webkitURL;

document.getElementById('fileUp').onchange = setFileInfo;

function setFileInfo() {
  var files = this.files;
  myVideos.push(files[0]);
  var video = document.createElement('video');
  video.preload = 'metadata';

  video.onloadedmetadata = function() {
    window.URL.revokeObjectURL(video.src);
    var duration = video.duration;
    myVideos[myVideos.length - 1].duration = duration;
    updateInfos();
  }

  video.src = URL.createObjectURL(files[0]);;
}


function updateInfos() {
  var infos = document.getElementById('infos');
  infos.textContent = "";
  for (var i = 0; i < myVideos.length; i++) {
    infos.textContent += myVideos[i].name + " duration: " + myVideos[i].duration + '\n';
  }
}
<div id="input-upload-file" class="box-shadow">
  <span>upload! (ღ˘⌣˘ღ)</span>
  <input type="file" class="upload" id="fileUp" name="fileUpload">
</div>
<pre id="infos"></pre>

【讨论】:

  • 这个效果很好,但是在我返回值之前它没问题,然后在我返回之后它是未定义的。知道我能做些什么来解决这个问题吗?即 24.105 未定义
  • 是的,将其存储在全局变量中,或者更好的是,将文件存储在数组中,然后在事件处理程序中,将持续时间属性添加到存储的文件中
  • @davis,查看编辑,然后您可以使用myVideos.splice(the_index_of_the_video)删除不需要的文件
  • 哦,这对我帮助很大,谢谢!另外,为什么我不能将此信息发送到函数之外?即使使用 getter 和 setter,我仍然有问题。
  • 因为你在视频的onmetadatascope,异步工作。您必须将其存储在全局变量中(就像我对myVideos = []; 所做的那样),并且只有在触发此事件后才调用您的setFileInfo()。或者,您可以使用setTimeout() 循环来检查属性是否已设置。
【解决方案2】:

我需要在继续执行更多代码之前验证单个文件,这是我在 Kaiido 回答的帮助下的方法!

用户上传文件时的onchange事件:

$("input[type=file]").on("change", function(e) {

    var file = this.files[0]; // Get uploaded file

    validateFile(file) // Validate Duration

    e.target.value = ''; // Clear value to allow new uploads
})

现在验证持续时间:

function validateFile(file) {

    var video = document.createElement('video');
    video.preload = 'metadata';

    video.onloadedmetadata = function() {

        window.URL.revokeObjectURL(video.src);

        if (video.duration < 1) {

            console.log("Invalid Video! video is less than 1 second");
            return;
        }

        methodToCallIfValid();
    }

    video.src = URL.createObjectURL(file);
}

【讨论】:

    【解决方案3】:

    这是 async/await Promise 版本:

    const loadVideo = file => new Promise((resolve, reject) => {
        try {
            let video = document.createElement('video')
            video.preload = 'metadata'
    
            video.onloadedmetadata = function () {
                resolve(this)
            }
    
            video.onerror = function () {
                reject("Invalid video. Please select a video file.")
            }
    
            video.src = window.URL.createObjectURL(file)
        } catch (e) {
            reject(e)
        }
    })
    

    可以如下使用:

    const video = await loadVideo(e.currentTarget.files[0])
    console.log(video.duration)
    

    【讨论】:

    • typescript 抱怨将this 传递给resolve,如果您将loadVideo 的返回类型指定为Promise&lt;HTMLVideoElement&gt;。抛出错误 Argument of type 'GlobalEventHandlers' is not assignable to parameter of type 'HTMLVideoElement | PromiseLike&lt;HTMLVideoElement&gt;' 要解决此问题,只需将 video 对象本身传递给 resolve 而不是 this,就像这样 resolve(video)
    【解决方案4】:

    FileReader 获取视频时长很简单,在async/await 中也很容易管理。

    const getVideoDuration = file =>
      new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => {
          const media = new Audio(reader.result);
          media.onloadedmetadata = () => resolve(media.duration);
        };
        reader.readAsDataURL(file);
        reader.onerror = error => reject(error);
      });
    
    const duration = await getVideoDuraion(file);
    

    其中fileFile 对象

    现场示例

    const getVideoDuration = (file) =>
      new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => {
          const media = new Audio(reader.result);
          media.onloadedmetadata = () => resolve(media.duration);
        };
        reader.readAsDataURL(file);
        reader.onerror = (error) => reject(error);
      });
    
    const handleChange = async (e) => {
      const duration = await getVideoDuration(e.target.files[0]);
      document.querySelector("#duration").innerText = `Duration: ${duration}`;
    };
    <div>
      <input type="file" onchange="handleChange(event)" />
      <p id="duration">Duration: </p>
    </div>

    【讨论】:

    • 你为什么写“For React”?我不明白这与 React 有什么关系,它只是 JS ......我鼓励你从你的答案中删除该行。
    • 是的,我知道与 javascript 没有任何关系,但我添加了一个 react 演示,这就是原因,但感谢您的提问,我会将演示转换为纯 vanila js
    • 如果文件大小超过 500mb,浏览器就会卡住。最好使用其他帖子中提到的 video.duration。
    【解决方案5】:

    这就是我在将视频推送到 S3 之前设法获得视频持续时间的方法。 我正在使用此代码上传 4+ GB 的视频文件。

    注意 - 对于 .avi、.flv、.vob、.mpeg 等格式,将找不到持续时间,因此请通过给用户的简单消息来处理它

      //same method can be used for images/audio too, making some little changes
      getVideoDuration = async (f) => {
        const fileCallbackToPromise = (fileObj) => {
          return Promise.race([
            new Promise((resolve) => {
              if (fileObj instanceof HTMLImageElement) fileObj.onload = resolve;
              else fileObj.onloadedmetadata = resolve;
            }),
            new Promise((_, reject) => {
              setTimeout(reject, 1000);
            }),
          ]);
        };
    
        const objectUrl = URL.createObjectURL(f);
        // const isVideo = type.startsWith('video/');
        const video = document.createElement("video");
        video.src = objectUrl;
        await fileCallbackToPromise(video);
        return {
          duration: video.duration,
          width: video.videoWidth,
          height: video.videoHeight,
        };
    }
    
    
    //call the function
    //removing unwanted code for clarity
    const meta = await this.getVideoDuration(file);
    //meta.width, meta.height, meta.duration is ready for you to use
    

    【讨论】:

    • 知道为什么您在笔记中提到的格式缺少视频时长吗?实际上,对于这些格式,onload 和 onloadedmetadata 事件甚至都没有触发(用 video/avi 测试)。
    • 是的...它没有丢失,但我不想添加它,因为有许多格式的视频文件,如 avi、mpeg 等,获取其中一些视频文件的持续时间失败,而其余的它们很容易处理,因为它更多地以浏览器为中心。
    • 我在这里问过一个类似的问题 (stackoverflow.com/questions/70133389/…),有些人提到在客户端获取文件元数据(我猜是通过创建视频元素并将文件 blob 设置为源) , 是一个安全漏洞。是这样吗?
    • 我同意@FloRagossnig,但很多时候大多数库都不太了解视频格式的持续时间细节,因此这是明确获取持续时间的方法之一。
    猜你喜欢
    • 2021-05-21
    • 1970-01-01
    • 2016-04-09
    • 2011-04-25
    • 1970-01-01
    • 1970-01-01
    • 2021-02-21
    • 1970-01-01
    相关资源
    最近更新 更多