I am working on a project (server side) where i need to stream data (videos, large files) to clients.
This worked perfect using ByteRangeStreamContent
, as i was serving files from disk and could create a seekable stream (FileStream
).
if (Request.Headers.Range != null)
{
try
{
HttpResponseMessage partialResponse = Request.CreateResponse(HttpStatusCode.PartialContent);
partialResponse.Content = new ByteRangeStreamContent(fs, Request.Headers.Range, mediaType);
return partialResponse;
}
catch (InvalidByteRangeException invalidByteRangeException)
{
return Request.CreateErrorResponse(invalidByteRangeException);
}
}
else
{
response.Content = new StreamContent(fs);
response.Content.Headers.ContentType = mediaType;
return response;
}
But, i moved the file provider from disk to an external service. The service allows me to get chunks of data (Range{0}-{1}).
Of course, it's not possible to download whole file in memory and then use a MemoryStream
for ByteRangeStreamContent
because of the obvious reasons (too many concurrent downloads will consume all the available memory at some point).
I found this article https://vikingerik.wordpress.com/2014/09/28/progressive-download-support-in-asp-net-web-api/ where the author says:
A change request I got for my library was to support reading only the necessary data and sending that out rather than opening a stream for the full data. I wasn’t sure what this would buy until the user pointed out they are reading their resource data from a WCF stream which does not support seeking and would need to read the whole stream into a MemoryStream in order to allow the library to generate the output.
That limitation still exists in this specific object but there is a workaround. Instead of using a ByteRangeStreamContent, you could instead use a ByteArrayContent object instead. Since the majority of RANGE requests will be for a single start and end byte, you could pull the range from the HttpRequestMessage, retrieve only the bytes you need and send it back out as a byte stream. You’ll also need to add the CONTENT-RANGE header and set the response code to 206 (PartialContent) but this could be a viable alternative (though I haven’t tested it) for users who do not want or can’t easily get a compliant stream object.
So, my question basically is: how can i do that ?
I finally managed to do it.
Here's how:
Custom implementation of a
stream
:Usage: