I have several Actions in my MVC Controllers that return an Azure blob by passing a blob stream into a FileResult. Like this:
public FileResult DownloadReport(string Id)
{
// Look up model
string fileName = messenger.ReportTitle(Id);
// Get a reference to the blob
CloudBlockBlob mainReportBlob = cloudBlobContainer.GetBlockBlobReference(Id);
// Return as a FileResult
using (var reportReader = mainReportBlob.OpenRead())
{
return File(reportReader , "application/pdf", fileName );
}
}
I recently updated my Azure Storage library to the latest and started receiving the following exceptions:
System.NullReferenceException was unhandled by user code
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=Microsoft.WindowsAzure.Storage
StackTrace:
at Microsoft.WindowsAzure.Storage.Blob.BlobReadStreamBase.ConsumeBuffer(Byte[] buffer, Int32 offset, Int32 count) in e:\projects\azure-sdk-for-net\microsoft-azure-api\Services\Storage\Lib\Common\Blob\BlobReadStreamBase.cs:line 222
at Microsoft.WindowsAzure.Storage.Blob.BlobReadStream.Read(Byte[] buffer, Int32 offset, Int32 count) in e:\projects\azure-sdk-for-net\microsoft-azure-api\Services\Storage\Lib\DotNetCommon\Blob\BlobReadStream.cs:line 72
at System.Web.Mvc.FileStreamResult.WriteFile(HttpResponseBase response)
at System.Web.Mvc.FileResult.ExecuteResult(ControllerContext context)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList
The error seems to be coming from within the Azure libraries themselves. Any ideas?
So after stepping thru things with a decompiler, I found the BlobReadStreamBase.Dispose()
method was being called before it had been read. Digging further, it seems that returning File()
from a controller doesn't actually read the stream, but simply passes it up to the rest of MVC to deal with it.
What this means is that my using()
block was closing the Blob stream before MVC could read it further down the pipeline. I changed my Action to this:
public FileResult DownloadReport(string Id)
{
// Look up model
string fileName = messenger.ReportTitle(Id);
// Get a reference to the blob
CloudBlockBlob mainReportBlob = cloudBlobContainer.GetBlockBlobReference(Id);
// Return as a FileResult, DON'T place in a using() block or it will be closed early
var reportReader = mainReportBlob.OpenRead();
return File(reportReader , "application/pdf", fileName );
}
This works. Furthermore, I set a breakpoint in my decompiler and BlobReadStreamBase.Dispose() is being called later, so presumably MVC is cleaning things up further down the pipeline.
I'm not sure why this worked before, as it shouldn't have. Maybe a bug that was fixed in the Azure libraries?
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