HTML5 视频和音频缓存问题

4

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

 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();
    })
}

它的表现非常好,可以成功加载我提供的所有媒体。唯一的问题是在Chrome浏览器中,当我引用一个已经预加载的
1个回答

3

我不确定缓存问题是否是Chrome的bug,但你所做的事情对我来说似乎非常奇怪。

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

当我们通过mediaElement(<audio><video>)加载媒体时,浏览器会进行range请求,即它不会下载完整的文件,而只会下载播放所需的内容,以避免中断。
这就是为什么您会得到206部分内容响应。这也可能是Chrome不识别它为相同请求的原因,因此不使用缓存再次强调我不确定这是否是Chrome的bug

但既然您已经下载了完整的文件,为什么不将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
@Zze,好的,但是我想说的是,在这里你没有使用已下载的数据,而是在向服务器发起新请求。所以这可能是Chrome中未使用缓存的一个bug,但是你可以强制使用你已下载的数据,这样更有意义(遵循这个建议,所有用户代理将按相同方式处理)。 - Kaiido
你说的对,我理解了 - 我现在将尝试实现这个并会回复你。另外,我之前没听过 UA 这个术语 - 你能否给我解释一下? - Zze
UA => 用户代理(通常指网络浏览器,但某些其他类型的软件也会实现一些WebAPI,因此我更喜欢引用通用的UA缩写,虽然我不知道任何实现媒体API而不是网络浏览器的UA ;-P)。 - Kaiido
你太棒了 - 感谢你的帮助,现在它在所有浏览器中都完美运行! - Zze
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接