2

I would like to download a image from a url and serve it via a controller. Ideally I want to stream as much as possible rather than attempting to create byte arrays etc.

The reason I'm downloading file via my controller, rather than just giving foo.ImageUrl to the consumer is because the urls are http and I'm proxying to avoid mixed content. This actions gets called by putting the absolute url to it into html img tags

This is what I have so far:

The problem is that the image doesn't seem to have any content. The img tag looks blank and when I navigate to it in my browser I just see the headers and nothing tries to download?

How do I make downloading from a url and returning it as a stream to the caller work?

[HttpGet]
[AllowAnonymous]
[Route(template: "Reward/FooLogo/{fooId}/bar/{barId}", Name = "FooLogo")]
public async Task<StreamContent> FooLogo(int fooId, int barId)
{
    var foo = await GetFooAsync(fooId, barId);
    if (string.IsNullOrWhiteSpace(foo?.ImageUrl))
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    using (var response = await WebRequest.Create(foo.ImageUrl).GetResponseAsync())
    {
        // todo check content type
        var responseStream = response.GetResponseStream();

        var content = new StreamContent(responseStream);
        content.Headers.ContentType = new MediaTypeHeaderValue(response.ContentType);
        content.Headers.ContentLength = response.ContentLength;

        return content;
    }
}
Nathan Cooper
  • 6,262
  • 4
  • 36
  • 75
  • http://stackoverflow.com/a/9549889/4149098 does not work for you? – Leszek P Feb 23 '17 at 12:26
  • In your code you create a web request stream and then return the stream via StreamContent and then dispose of the underlying connection (in your using block). Of course the caller can never do anything with it, the underlying stream is disposed of when the result is returned from the `FooLogic` call. – Igor Feb 23 '17 at 12:28
  • Probably the response stream is being disposed of before the content can be delivered. Copy the content to a new stream and return that stream – Nkosi Feb 23 '17 at 12:28
  • Possible duplicate of [Returning binary file from controller in ASP.NET Web API](http://stackoverflow.com/questions/9541351/returning-binary-file-from-controller-in-asp-net-web-api) – Igor Feb 23 '17 at 12:29
  • Hmm. So StreamContent takes responsibility for disposing of the request and I shouldn't be doing it. I'll check that out. – Nathan Cooper Feb 23 '17 at 12:32

1 Answers1

4

Just removing the using didn't seem to fix my problem. I've re-written it a bit, and this seems to have fixed my problem.

[HttpGet]
[AllowAnonymous]
[Route(template: "Reward/FooLogo/{fooId}/bar/{barId}", Name = "FooLogo")]
public async Task<HttpResponseMessage> FooLogo(int fooId, int barId)
{
    var foo = await GetFooAsync(fooId, barId);
    if (string.IsNullOrWhiteSpace(foo?.ImageUrl))
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    using (var client = new HttpClient())
    {
        var res = await client.GetAsync(paymentMethod.ImageUrl);
        var response = Request.CreateResponse(HttpStatusCode.OK);
        response.Content = new StreamContent(await res.Content.ReadAsStreamAsync());
        response.Content.Headers.ContentType = res.Content.Headers.ContentType;

        return response;
    }
}
Nathan Cooper
  • 6,262
  • 4
  • 36
  • 75