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

Posted

技术标签:

【中文标题】如何将对 REST 调用的响应分成小块?【英文标题】:How to chop up response to REST-call into small chunks? 【发布时间】:2016-07-17 18:06:27 【问题描述】:

我正在使用 node.js 开发一个 Web 应用程序。应用程序的某些部分允许用户上传/检索文件。我使用“SoftLayer 对象存储”服务来托管这些文件。但是,在提供视频时,它在 Chrome/Firefox/android 上的 html5-video 元素中可以正常播放,但在 Safari/ios 上却不行。

为了深入了解这个问题,我在另一个 ***-post 上遇到了这个解释:

iOS 设备希望视频以小块形式出现。因此,例如流媒体服务器能够做到这一点。但是,blob 服务器只是将视频作为 blob 处理,这不是 iOS 设备所期望的。有些浏览器足够聪明,可以处理这个问题,但有些则不然。

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

不幸的是,这个流代码在我的情况下不起作用,因为该文件托管在 SoftLayer 上,而接受假设该文件存在于服务器的文件系统上。我通过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);
  );
);

显然,此代码不会“流式传输”视频,而是一次将整个视频发送到客户端。使用此代码,视频无法在 Safari 中播放。

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

最后我还想提一下,我不认为视频的编码是问题所在。我正在使用这个视频http://www.w3schools.com/html/mov_bbb.mp4,当我在 iOS/safari 上输入该链接时,它工作得很好。

【问题讨论】:

【参考方案1】:

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

相反,您需要将响应正文转换为可读流并将其通过管道传递给响应。要将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);
      
    );
  );
);

【讨论】:

感谢您的评论。我相信你让我走上了正确的轨道,但我希望你能给我更多的指导,因为流缓冲区的文档非常少。我现在的代码看起来像这样 (pastebin.com/X7LNj7Qa) 但它不起作用。我不确定我是否以正确的方式将数据放在 ReadableStreamBuffer 上(第 23 行),是否以正确的方式设置标题(第 12 行)以及是否以正确的方式管道传输缓冲区(第 27 行)。 我正在通过一个网页进行测试,该网页包含一个以“/testFile”端点为源的 HTML5 视频元素。 “写入数据”被多次写入控制台(因为第 26 行),所以我很确定我将数据放在 bodyStream 上。然而,视频根本没有播放,node.js 给出了以下警告:检测到可能的 EventEmitter 内存泄漏。增加了 11 位听众。使用emitter.setMaxListeners() 增加限制。 @Simon nonono,您不需要添加“数据”侦听器。只需将 bodyStream 管道传输到 res 并放置数据。我已经编辑了答案,现在应该有正确的代码 @Petr 其他人指出选项“encoding:null”也应该包含在请求调用中。添加后,电影在 chrome 和 firefox 中运行。它在 safari 中仍然不起作用,但这超出了这个问题的范围。感谢您的帮助!

以上是关于如何将对 REST 调用的响应分成小块?的主要内容,如果未能解决你的问题,请参考以下文章

如何在java中进行rest api调用并映射响应对象?

如何从 REST API 获取响应并从 jquery ajax 调用执行操作?

如果我必须进行REST调用并将响应值与UI进行比较,那么步骤定义将如何构建?

在节点中创建 REST API 时,如何将来自对外部网站的请求的 http 响应流式传输到原始 api 调用?

提高调用其他 API 的 Rest Webservice 性能的方法

如何在rest api调用中构造json并使其同步