views:

33

answers:

1

I put together the following method, which calls my repository to get an image (returned as Linq.Binary). It then writes the byte array to the OutputStream and sets the content type:

    public EmptyResult GetImage(int id)
    {
        Byte[] imageBytes = _repository.GetImage(id).ToArray();

        Response.OutputStream.Write( imageBytes, 0, imageBytes.Length);
        Response.ContentType = "image/png";

        return new EmptyResult();
    }

This is one of those times where I wrote a block of code, it worked first try (always scary), and now I'm trying to think through where it will break.

My requirements are just to return an image that I'm getting from the database, no text.

What I'm doing above allows me to avoid the use of converting to a Bitmap (or other Image type), I don't need to have references to System.Drawing, I don't need to write a custom ActionResult and I don't need to worry about disposing resources.

I'd have to think a little further through, but I think this also opens the door to leveraging the MVC OutputCache without much pain.

I see people writing custom ActionResults and am trying to figure out the benefit. I mean, I understand the additional flexibility and ability to handle multiple types, but one solution I found took the Binary.ToArray(), converted to a Bitmap and created an ashx handler to return the image. Is any of that necessary?

Is it bad form to write to the output stream when you're returning an EmptyResult?

In all current browsers, the above code allows me to display an image with a simple:

 <img id="display-image" 
      src="<%= Url.Action("GetImage", new { id = Model.ImageID }) %>" />

So are their downfalls to this approach?

Thanks for thoughts/comments folks.

Cheers, -james

+1  A: 

Is it bad form to write to the output stream when you're returning an EmptyResult?

Yes, but there are plenty of more appropriate result types, such as FileContentResult and FileStreamResult.

Many people write custom ActionResults because:

  • They started with an early version of MVC, which didn't have these, or
  • They have highly specific needs not met by the built-in types
  • They don't want to have to remember to set the mime-type correctly every time, so they build in a type for a specific mime-type (e.g., the built-in JavaScriptResult).
  • Writing to Response makes controllers hard to test. You can write in an ActionResult.ExecuteResult instead and have a testable controller.

You don't need a custom result type for an image, but you could write one which wraps up the two lines of code you use to write to Response and set content type.

E.g., something like:

public class PngResult : ActionResult {

    public Byte[] Data {
        get;
        set;
    }

    public override void ExecuteResult(ControllerContext context) {
        if (context == null) {
            throw new ArgumentNullException("context");
        }

        if (Data == null)
        {
            throw new InvalidOperationException("Data is null.");
        }

        HttpResponseBase response = context.HttpContext.Response;
        response.ContentType = "image/png";

        response.OutputStream.Write(Data);
    }

    public PngResult(Byte[] data) 
    {
        if (data == null) {
            throw new ArgumentNullException("data");
        }
        this.Data = data;
    }
}

Update: On second thought, you should maybe subtype FileContentResult rather than ActionResult and you'd have even less code to write. But you get the idea.

Craig Stuntz
Thanks Craig, I've got the FileContentResult version going and it was trivial. Cheers! -james
MisterJames
By the way, your last point (on testability) is what sold it for me. I'm not too worried about setting content type and what not, but it is a little more clear to return a new something-or-other and when it adds some flexibility in testing you're that much further ahead. Cheers! -james
MisterJames