Given a Range request like this:
curl -r 0-16 https://example.com/api/blob/mobydick.txt -o moby0.txt -D -
We get:
Call me Ishmael.
But a Range request like this:
curl -r 16-32 https://example.com/api/blob/mobydick.txt -o moby1.txt -D -
We still get:
Call me Ishmael.
This is with the following code (edited to remove getting the blob, accounting for requests without range headers, or with an open-ended range, etc):
Stream myBlobStream = await myBlob.OpenReadAsync();
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.PartialContent);
message.Content = new ByteRangeStreamContent(myBlobStream , range, myBlob.Properties.ContentType);
return message;
In samples I've seen, it doesn't appear to be necessary to manually Seek or set the Position of the stream, because (as I understand it) that should be handled by ByteRangeStreamContent. When I do try to manually set the Position of the stream to the beginning of the Range, the result is inconsistent; sometimes I end up with a one-byte text file, and sometimes with the entire file starting from the beginning of the Range (i.e. so the end of the Range is ignored).
At least for now, I've solved this by returning ByteArrayContent instead of ByteRangeStreamContent.
// Including my setup of the range values this time:
var range = Request.Headers.Range;
long chunkLength = 2500000;
long? beginRange = range.Ranges.First().From;
long? endRange = range.Ranges.First().To;
if (endRange == null)
{
if ((beginRange + chunkLength) > myBlob.Properties.Length)
{
endRange = myBlob.Properties.Length - 1;
}
else
{
endRange = beginRange + chunkLength;
}
}
var blobStreamPosition = beginRange.Value;
// Set the stream position
blobStream.Position = blobStreamPosition;
int bytesToRead = (int)(endRange - blobStreamPosition + 1);
// Using BinaryReader for convenience
BinaryReader binaryReader = new BinaryReader(blobStream);
byte[] blobByteArray = binaryReader.ReadBytes(bytesToRead);
message.Content = new ByteArrayContent(blobByteArray);
// Don't forget that now you have to set the content range header yourself:
message.Content.Headers.ContentRange = new ContentRangeHeaderValue(blobStreamPosition, endRange.Value, myBlob.Properties.Length);
message.Content.Headers.ContentType = new MediaTypeHeaderValue(myBlob.Properties.ContentType);
binaryReader.Dispose();
blobStream.Dispose();
I honestly don't know what issues might be lurking in this solution; if nothing else, that byte array means that it should probably include a limit on the size of partial response it'll return. I'd rather use the ByteRangeStreamContent, but this seems to be working for us.