如何将对 REST 调用的响应分成小块?

How to chop up response to REST-call into small chunks?

我正在 node.js 开发网络应用程序。该应用程序的某些部分允许用户访问 upload/retrieve 文件。我使用 'SoftLayer Object Storage'-服务来托管这些文件。但是,在提供视频时,它在 Chrome/Firefox/Android 上的 HTML5-video 元素中播放正常,但在 Safari/iOS.

上却没有

为了深入了解这个问题,我 运行 在另一个 Whosebug 上进行了解释-post:

iOS devices expect the videos to arrive in small chunks. So for instance a streaming server is able to do this. However, a blob server just hands the video as a blob which is not what the iOS device expects. Some browsers are smart enough to handle this but others not.

为了检查这个解释是否有意义,我尝试流式传输相同的 mp4 文件。为了做到这一点,我基于 this Whosebug-post 上接受的答案,这实际上在所有浏览器和平台上都运行良好,这让我相信我上面引用的解释实际上是正确的。

不幸的是,此流式代码在我的情况下不起作用,因为该文件托管在 SoftLayer 上,而 accepted 假定该文件存在于服务器的文件系统上。我通过 this API 与 "Softlayer Object Store" 交流。我现在的代码如下所示:

router.get('/testFile', function (req, res) {
  res.writeHead(200, {'Content-Type' : 'video/mp4','Accept-Ranges': 'bytes'});
  request.get({url: authEndpoint, headers: {"X-Auth-Key": apiKey, "X-Auth-User": user}}, function (err, res1) {
    var data = JSON.parse(res1.body);
    var objectPath = data.storage.public + '/' + container + '/' + filename;
    request.get({
      url: objectPath,
      headers: {"X-Auth-Token": res1.headers['x-auth-token']}
    }, function (err) {
      if (err) {
        console.log('error', err);
      }
    }).pipe(res);
  });
});

显然,此代码不会 "stream" 视频,只是将整个视频一次发送给客户端。使用此代码,视频无法在 Safari 中播放。

我想以某种方式逐块发送视频(就像上面讨论的 Whosebug-answer 中的情况一样)而不是一次全部发送,但是当通过REST 请求。我确实注意到 API 允许在执行 GET 请求时提供可选的 "Range" 参数,但我不确定如何利用它来发挥我的优势。

最后我还想提一下,我不认为视频的编码是问题所在。我正在使用此视频 http://www.w3schools.com/html/mov_bbb.mp4,当我在 iOS/safari 上输入 link 时,效果很好。

问题是您正在尝试 pipe 来自 API 的整个响应,包括状态和 headers 等等。

相反,您需要将响应 body 转换为可读流并将其通过管道传递给响应。要将 Buffer 转换为 ReadableStream,您可以使用 stream-buffers

这样的库

所以,您的代码应该看起来像这样:

router.get('/testFile', function (req, res) {
  res.writeHead(200, {'Content-Type' : 'video/mp4','Accept-Ranges': 'bytes'});
  request.get({url: authEndpoint, headers: {"X-Auth-Key": apiKey, "X-Auth-User": user}}, function (err, res1) {
    var data = JSON.parse(res1.body);
    var objectPath = data.storage.public + '/' + container + '/' + filename;
    request.get({
      url: objectPath,
      headers: {
        "X-Auth-Token": res1.headers['x-auth-token']
      }
    }, function (err, data) {
      if (err) {
        console.log('error', err);
      } else {
        var bodyStream = new streamBuffers.ReadableStreamBuffer({
          frequency: 10,   // in milliseconds. 
          chunkSize: 2048  // in bytes. 
        });
        bodyStream.pipe(res);
        bodyStream.put(data.body);
      }
    });
  });
});