We have to send portion of file based on specified HttpRange
in request. In .Net core we can return any object which inherits from FileResult
and this class has a property for specifying whether range processing must be enabled or not:
/// <summary>
/// Gets or sets the value that enables range processing for the <see cref="FileResult"/>.
/// </summary>
public bool EnableRangeProcessing { get; set; }
And we can return those objects and framework will handle requested "http range" value automatically. So far so better. But, they consciously decided to not support multiple byte ranges in one request and in such case whole file content will be sent as a response. (here is the link to my SO question regarding this issue)
And in our case client's want to send multiple byte ranges in one request.
For that reason, I have two options. Either to create new action result type which supports both single and multiple ranges. Or to use Microsoft.AspNetCore.Mvc.WebApiCompatShim
package which is developed by Microsoft and provides compatibility in ASP.NET Core MVC with ASP.NET Web API 2 to simplify migration of existing Web API implementations.
I already created some custom type for supporting multiple ranges, but it has to be refactored, tested. That's why I thought that may be the 2nd approach is much more relevant for this scenario.
So, here are steps. First, I installed that package. Then added call for AddWebApiConventions
. This will hook up the HttpResponseMessageOutputFormatter
to help and hint the Json
serializer to do it’s serialization magic properly.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddWebApiConventions();
}
And then just created new ApiController
which creates response based on ByteRangeStreamContent
:
public class FileController : ApiController
{
[HttpGet]
[HttpHead]
[Route("download")]
public ResponseMessageResult Download(string filePath)
{
var response = Request.CreateResponse(HttpStatusCode.PartialContent);
var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
response.Content = new ByteRangeStreamContent(fileStream, Request.Headers.Range, MediaTypeNames.Application.Octet);
return ResponseMessage(response);
}
}
That endpoint can execute both single and multiple http range request in .Net. But, in .net core it again supports only single byte range response.
Let's say i have file with such content:
ABCDFGHIJKLMNOPQRSYUVWXYZ
In case of single byte-range request the response is:
curl -H Range:bytes=1-5 http://localhost:55348/File/download?filePath=D:\Request.txt -i
ABCDF
But, incase of multiple byte-range request it strangely return full content for first range:
curl -H Range:bytes=1-5,6-8 http://localhost:55348/File/download?filePath=D:\Request.txt -i
--51239b4a-9749-481f-a353-6cad668fb7dc
Content-Type: application/octet-stream
Content-Range: bytes 1-5/25
ABCDFGHIJKLMNOPQRSYUVWXYZ -- As you see full content is returned instead of `ABCDF`
--51239b4a-9749-481f-a353-6cad668fb7dc
Content-Type: application/octet-stream
Content-Range: bytes 6-8/25
Can't understand why it can't create response appropriately in .net Core.