如何从 ServiceWorker 流式传输视频?

How can I stream a video from a ServiceWorker?

我有一个 service worker 可以捕获我页面的请求(fetch 事件),当 URL 匹配特定模式时,它会获取另一个 URL 并替换响应有了那个新内容。 它非常适合文本数据(JS,XML...)或二进制数据(例如图像),但是当涉及到视频时,就会出现故障。

我在 OSX 上使用 Chrome 41。

这是我工人的简化代码:

self.addEventListener('fetch', function(event) {
  var url = event.request.url;
  console.log('SW: fetch', url);
  if (/\.mp4$/.test(url)) {
    url = 'https://vjs.zencdn.net/v/oceans.mp4';
    var options = {
      credentials: 'include',
      mode: 'no-cors'
    };
    event.respondWith(fetch(url, options));
  }
});

这是我的 HTML 页面中的简化代码:

navigator.serviceWorker.register('sw.js').then(function(reg) {
  console.info('ServiceWorker registration successful for', reg.scope);

  var video = document.createElement('video');
  video.src = '/video.mp4';
  video.controls = true;
  video.autoplay = true;
  video.onerror = function(err) {
    console.error('Video load fail', err);
  }
  video.onload = function(data) {
    console.info('Video load success');
  }
  document.body.appendChild(video);

}).catch(function(err) {
  console.error('ServiceWorker registration failed:', err);
});

第一次加载页面,worker安装,所以没有抓到视频请求,因此失败。但是当你重新加载页面时(没有清理缓存),它被成功捕获,工作人员成功加载视频(其检查器中的 HTTP 200),但由于某种原因,主页面抛出 net::ERR_FAILED.

而且我无法手动 read/stream 它,因为它来自不同的来源,导致 "opaque" 响应类型:http://www.w3.org/TR/service-workers/#cross-origin-resources

更新:更新到 Chrome 42,错误现在是 HTTP 400 Service Worker Fallback Required (from ServiceWorker)。奇怪的是 the source code (第 510 行)表示只有在缺少 CORS headers 时才应该引发它,但这里的模式是 no-cors.

首先,我建议尝试使用当前的 Chrome Canary,它当前对应于版本 44.0.2371.0。随着每个新 Chrome 版本的发布,围绕服务工作者的开发人员工具不断改进,并且总体上在 43+ 版本中变得更好。

看起来 opaque Response objects 不能用于 <video> 元素的 src,我假设这是故意的。 (不透明的 Responses 可以用于某些目的,恐怕我没有完整的说明什么可以和不可以与不透明的 Response 一起使用。)

但无论如何,您很幸运 vjs.zencdn.net 支持 CORS,因此您可以使用默认值 CORS-enabled Request,这会给您带来non-opaque Response.

你不能做的一件事是在你的 CORS Request 中使用 credentials: 'include',因为它会导致 Fetch API cannot load https://vjs.zencdn.net/v/oceans.mp4。当凭证标志为 true. 错误时,不能在 'Access-Control-Allow-Origin' header 中使用通配符“*”。这似乎不是您的特定主机的问题,因为不需要凭据。

当我将您的代码切换为使用 event.respondWith(fetch('https://vjs.zencdn.net/v/oceans.mp4')); 时一切正常。