Instead of returning from blobclient.OpenRead
, you can return a wrapped stream using below code. Our requirement is to enable parallel and resumable Azure blob downloads from loadbalanced fileservers. There might be some issues in code, please review it and fix them as required.
When returning controller.File(stream, contentType, filename, true)
in controller, since last parameter enableRangeProcessing=true
, it verifies stream.CanSeek=true
and calls stream.Seek
with incoming contentrangeheader's range.from
value.
So wrap the source with return new SeekableBlobReadStream(blobclient)
and send it to controller.
internal class SeekableBlobReadStream : Stream
{
private Stream stream;
private readonly BlobBaseClient client;
private readonly BlobProperties properties;
public SeekableBlobReadStream(BlobBaseClient client)
{
properties = client.GetProperties().Value;
this.client = client;
}
private void PrepareStream(long position = 0)
{
if (stream == null || position != 0)
{
if (stream != null)
stream.Dispose();
stream = client.OpenRead(new BlobOpenReadOptions(false) { Position = position });
}
}
public override bool CanRead => true;
public override bool CanSeek => true;
public override bool CanWrite => false;
public override long Length => properties.ContentLength;
public override long Position
{
get => (stream?.Position).GetValueOrDefault();
set => PrepareStream(value);
}
public override void Flush()
{
PrepareStream();
stream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
PrepareStream();
return stream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Current:
offset = Position + offset;
break;
case SeekOrigin.End:
offset = Length - offset;
break;
}
PrepareStream(offset);
return stream.Position;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
}