【问题标题】:html5 video & audio cache issuehtml5视频和音频缓存问题
【发布时间】:2017-02-01 23:52:19
【问题描述】:

我编写了一个自定义媒体预加载器,它使用一系列XMLHttpRequests 在显示我的ng2 app 之前加载大量媒体。利益相关者要求所有媒体在使用应用程序之前完整下载。

 private loadFile(media: any) {
    return new Promise(function (resolve, reject) {
        var error: boolean = false;

        //for (var media of media.videos) {
        //TODO: Check how this loads.....
        //console.log("Now Loading video >> ", media, media.hasOwnProperty("path"));


        // Standard XHR to load an image
        var request = new XMLHttpRequest();
        request.open("GET", (<any>media).path);
        request.responseType = 'blob';

        // When the request loads, check whether it was successful
        request.onload = () => {
            if (request.status === 200) {
                resolve(request.response);
            }
            else
                // If it fails, reject the promise with a error message
                reject(Error('Media didn\'t load successfully; error code:' + request.statusText));
        };

        // If an error occurs
        request.onerror = () => {
            // Also deal with the case when the entire request fails to begin with
            // This is probably a network error, so reject the promise with an appropriate message
            reject(Error('There was a network error.'));
        };

        request.onreadystatechange = function () {
            if (request.readyState == 4) {
                console.log("Load Complete >> ", media, "from", request.status); // Another callback here
            }
        };

        // Every tick of the progress loader
        request.onprogress = data => console.log(data);

        // Send the request
        request.send();
    })
}

它运行良好并成功加载到我提供给它的所有媒体中。

我只有 1 个问题,那就是在 Chrome 中,当我引用已预加载的 &lt;video&gt;&lt;audio&gt; 时,它不会从缓存中提取它,相反,它会从服务器重新下载它。 (IE9 甚至从缓存中提取)


任何音频和视频元素将始终从服务器重新下载...
<video width="640px" height="auto" controls autoplay preload="auto">
    <source src="./app/assets/video/Awaiting%20Video%20Master.mov" type="video/mp4"/>
</video>

<audio controls autoplay preload="auto">
    <source src="./app/assets/audio/1_2_4_audio1.mp3" type="audio/mp3" />
</audio>

这将始终从缓存中加载...

&lt;img src="./app/assets/images/BTFG-BOLD_Fundamentals.png" /&gt;


这里有 2 张屏幕截图,一张来自 chrome,一张来自 edge,显示来自开发工具的网络活动(两者都在启动前重置了缓存)...

边缘

我注意到的主要区别是在渲染内容(预加载后)时,浏览器之间的请求状态是不同的。但是为什么会这样呢?

我发现 this SO 2013 年的帖子指出:

视频的缓冲方式取决于浏览器的实现,因此可能因浏览器而异。

各种浏览器可以使用不同的因素来决定保留或丢弃缓冲区的一部分。旧段、磁盘空间、内存和性能是典型因素。

这就是这里发生的事情吗?如果是这样,是否有人知道解决此问题的方法,以便 chrome 始终尝试从缓存中提取视频?

【问题讨论】:

    标签: javascript html google-chrome caching


    【解决方案1】:

    不确定缓存问题是否是 chrome 错误,但你所做的对我来说似乎很奇怪。

    您正在预加载您的媒体,或者实际上是完全下载它,然后将 mediaElement 设置为原始来源。

    当我们通过 mediaElement(&lt;audio&gt;&lt;video&gt;)加载媒体时,浏览器会发出 range 请求,即它不会下载完整的文件,而只会下载它需要不间断播放的文件。
    这就是您收到206 Partial content 回复的原因。这也可能是为什么 chrome 无法将其识别为相同的请求,因此不会再次使用缓存 我不确定这是否是 chrome 错误

    但是既然您已经下载了完整的文件,为什么不将您的 mediaElement 的src 设置为这个下载的文件呢?

    // since you are setting the hr reponseType to `'blob'`
    mediaElement.src = URL.createObjectURL(request.response);
    // don't forget to URL.revokeObjectURL(mediaElement.src) when loaded
    

    工作示例:(在我的 FF 上触发了一个奇怪的错误...)

    function loadVideo(url) {
      return new Promise((resolve, reject) => { // here we download it entirely
          let request = new XMLHttpRequest();
          request.responseType = 'blob';
          request.onload = (evt)=>resolve(request.response);
          request.onerror = reject;
          request.open('GET', url);
          request.send();
        }).then((blob)=> 
        	new Promise((resolve, reject)=>{
        		resolve(URL.createObjectURL(blob)); // return the blobURL directly
        		})
        	);
    
    }
    
    loadVideo('https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4')
      .then(blobUrl => { // now it's loaded
        document.body.className = 'loaded';
        let vid = document.querySelector('video');
        vid.src = blobUrl; // we just set our mediaElement's src to this blobURL
        vid.onload = () => URL.revokeObjectURL(blobUrl);
      }).catch((err) => console.log(err));
    video{
      display: none;
      }
    .loaded p{
      display: none;
      }
    .loaded video{
      display: unset;
      }
    <p>loading.. please wait</p>
    <video controls></video>

    或者使用 fetch API:

    function loadVideo(url) {
      return fetch(url)
        .then(resp => resp.blob())
        .then(blob => URL.createObjectURL(blob));
    }
    
    loadVideo('https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4')
      .then(blobUrl => { // now it's loaded
        document.body.className = 'loaded';
        let vid = document.querySelector('video');
        vid.src = blobUrl; // we just set our mediaElement's src to this blobURL
        vid.onload = () => URL.revokeObjectURL(blobUrl);
      }).catch((err) => console.log(err));
    video {
      display: none;
    }
    .loaded p {
      display: none;
    }
    .loaded video {
      display: unset;
    }
    <p>loading.. please wait</p>
    <video controls></video>

    【讨论】:

    • 感谢您的回复...不幸的是,在使用该应用程序之前,要求所有媒体都以完整的形式下载(我将在问题中添加此内容)。不过,我现在会尝试实施您的解决方案 - 干杯。
    • @Zze,好的,但我要说的是,这里你没有使用下载的数据,你正在向服务器发出新的请求。所以它不使用缓存可能是 chrome 的一个 bug,但你可以强制它使用你下载的数据,无论如何它更有意义(遵循这个建议,所有 UA 都会表现相同)。跨度>
    • 你所说的对我来说很有意义 - 我现在将尝试实施这一点,并会回复你。另外,我以前没听过 UA 这个词——你能帮我澄清一下吗?
    • UA => User Agent(s) 或大多数时候是 Web 浏览器,但其他一些类型的软件也确实实现了一些 WebAPI,所以我更喜欢参考到通用 UA 首字母缩略词,而我不知道有任何 UA 实现了不是 Web 浏览器的媒体 API;-P
    • 我几乎已经完成了这项工作,只是在与 sanitizing 新网址相关的角度方面存在一些问题......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-25
    相关资源
    最近更新 更多