We have a service running under .NET Core 2.0 that opens a stream from an Azure blob store, and returns it in an HttpResult. It works perfectly in 1.0.43, but in 1.0.44, the web browser receives an empty stream.
The funny thing is this: I was uncertain if the stream was OK, so I wrote it to a file and then set stream.Position = 0 before returning it, just to test. Now it also was delivered correctly to the client.
What has changed in 1.0.44 that could cause this change in behaviour?
using System.Threading.Tasks;
using Funq;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using ServiceStack;
using ServiceStack.Configuration;
using ServiceStack.Web;
namespace ServiceStackBlobStreaming
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build();
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseServiceStack(new AppHost());
app.Run(async context =>
{
await context.Response.WriteAsync(
@"<!doctype html>
<html lang=en>
<head>
<meta charset=utf-8>
<title>ImageTest</title>
</head>
<body>
<img src=""/img"">
</body>
</html>");
});
}
}
public class AppHost : AppHostBase
{
public AppHost() : base("StreamTest", typeof(AppHost).Assembly)
{
AppSettings = new AppSettings();
}
public override void Configure(Container container)
{
}
}
public class ImageService : Service
{
private readonly CloudBlobContainer _container;
public ImageService()
{
var storageAccount = CloudStorageAccount.Parse("long_connection_string");
var storageClient = storageAccount.CreateCloudBlobClient();
_container = storageClient.GetContainerReference("container_name");
}
public async Task<IHttpResult> Get(ImageRequest request)
{
var blobReference = _container.GetBlobReference("some_blob_reference");
var stream = await blobReference.OpenReadAsync();
// Uncommenting the following somehow makes it work
//using (FileStream fileStream = File.OpenWrite(@"C:\Temp\foo.jpg"))
//{
// stream.CopyTo(fileStream);
//}
//stream.Position = 0;
return new HttpResult(stream, "image/jpeg");
}
}
[Route("/img")]
public class ImageRequest
{
}
}
I’ve tracked the issue down to HttpResultusage of CopyToAsync() where it will throw an Object Disposed Exception but if we replaced it to use the sync CopyTo, i.e:
ResponseStream.CopyTo(responseStream);
It wont throw the exception and will write the image to the Response.
I’m still tracking down why it’s failing when we use CopyToAsync in HttpResult, but in the meantime you can rewrite your implementation to return the image with:
public async Task Get(ImageRequest request)
{
var blobReference = _container.GetBlobReference("some_blob_reference");
var stream = await blobReference.OpenReadAsync();
base.Response.ContentType = "image/jpeg";
await stream.CopyToAsync(base.Response.OutputStream);
}
This should be resolved with the latest v4.5.15 / v1.0.45 that’s now available on MyGet.
Where you can return an async Azure Blob Stream in a HttpResult if you prefer:
public async Task<IHttpResult> Get(ImageRequest request)
{
var blobReference = _container.GetBlobReference("some_blob_reference");
var stream = await blobReference.OpenReadAsync();
return new HttpResult(stream, "image/jpeg");
}