使用 Spring 框架和 Chrome 设置视频流

Setting up a video stream with Spring Framework and Chrome

我们正在编写一个 Spring 服务,使 HTTP 端点可用,通过它可以流式传输来自 Amazon S3 商店的视频(或音频)文件。基本思想是,您可以在 Google Chrome 地址栏中输入 url,该服务将从 S3 获取文件并将其流式传输,这样用户就可以可以立即开始观看而无需等待下载完成,并且用户可以单击视频进度条中的随机点并立即从该点开始观看视频。

根据我的理解,这在理论上应该可行,即 Chrome 开始下载文件。该服务以 HTTP 200 响应并包含一个 Accept-Ranges: bytes 和一个 Content-Length: filesize header。 filesize 是已知的,因为我们可以在不获取整个文件的情况下将其作为元数据从 S3 查询。包含这些 header 会导致浏览器取消下载,并使用 Range: bytes=0-whatever header 再次请求文件(其中 whatever 是 Chrome 的某个块大小决定)。该服务随后以 HTTP 206(部分内容)和请求的字节范围进行响应,我们可以轻松确定该字节范围,因为 S3 支持相同的范围协议。 Chrome 然后从服务请求连续的块,直到流结束。

在 Spring 方面,我们在 ResponseEntity<InputStreamResource> 中发送数据(根据 this SO answer)。

然而,我们在实践中观察到 while Chrome 在几百个字节后取消了它的第一个请求。但是,它会发送带有 Range: bytes=0- header 的第二个请求,有效地请求整个文件。服务器响应 HTTP 206。结果,只下载了几百字节的视频,视频显然没有开始播放。

有趣的是,在 Firefox 中一切正常。不幸的是,我们的应用程序需要支持 Chrome。我们是否遗漏了协议的某些部分?

事实证明我们在 Content-Range 响应 header 中有一个 off-by-one 错误。

语法为Content-Range: bytes start-end/total。对于 10total,如果你想获得整个范围,你需要指定 bytes 0-9/10,而不是 0-10/10,这就是我们所做的。

当然,由于真实文件的尺寸更大,而且这些文件中间的块的实际范围,与上一段中人为的示例相比,这个错误更难被注意到...ಠ_ಠ