We're writing a Spring service that makes an HTTP endpoint available through which a video (or audio) file from an Amazon S3 store can be streamed. The basic idea is that you can type in an url in the Google Chrome address bar, and the service will fetch the file from S3 and stream it, in such a way that the user can start watching immediately without having to wait for a download to complete, and that the user can click on a random spot in the video's progress bar and immediately start watching the video from that spot.
The way I understand this should work in theory, is that Chrome starts downloading the file. The service responds with HTTP 200 and includes an Accept-Ranges: bytes
and a Content-Length: filesize
header. The filesize
is known, because we can query that as metadata from S3 without fetching the entire file. Including these headers causes the browser to cancel the download, and request the file again with a Range: bytes=0-whatever
header (where whatever
is some chunk size that Chrome decides). The service then responds with HTTP 206 (Partial content) and the requested byte range, which we can determine easily because S3 supports the same range protocol. Chrome then requests successive chunks from the service, until the stream ends.
On the Spring side, we're sending the data out in a ResponseEntity<InputStreamResource>
(as per this SO answer).
However, we observe in practice that while Chrome's cancels its first request after a few hundred bytes. However, it sends a second request with a Range: bytes=0-
header, effectively asking for the entire file. The server responds with an HTTP 206. As a result, is has only downloaded a few hundred bytes of video, and the video obviously doesn't start playing.
Interestingly, in Firefox it all works properly. Unfortunately, our app needs to support Chrome. Are we missing some part of the protocol?
Interface StreamingResponseBody A controller method return value type for asynchronous request processing where the application can write directly to the response OutputStream without holding up the Servlet container thread.
Who uses Spring? 558 companies reportedly use Spring in their tech stacks, including Accenture, deleokorea, and Intuit.
It turns out we had an off-by-one error in the Content-Range
response header.
The syntax is Content-Range: bytes start-end/total
. With a total
of 10
, if you want to get the entire range, you need to specify bytes 0-9/10
, not 0-10/10
, which was what we were doing.
Of course with the larger sizes of real files, and the actual ranges of chunks in the middle of such files, this error was a lot harder to notice than in the contrived example in the previous paragraph... ಠ_ಠ
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With