ASP.Net MVC: Upload Image to Database and Show Image “Dynamically” Using a View

Oddly enough this came about from me wanting to do this, figuring it out, and then deciding not to bother with it. So there’s a possibility this will happen to you too. Well that’s not completely true. The first half where I was uploading and showing from a database, but showing an image through a view to mimic the .ashx functionality of WebForms is still pretty useful.

Saving the Image

First off, here’s the look of the table:

Table

So pretty simple table. Most important parts are the ImageData and ContentType. Why? Well let’s look at the action needed to save the image:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Upload(PhotoForSingleItem photo)
{
  //PhotoForSingleItem is just a class that has properties
  // Name and Alternate text.  I use strongly typed Views and Actions
  //  because I'm not a fan of using string to get the posted data from the
  //  FormCollection.  That just seems ugly and unreliable to me.

  //PhotoViewImage is just a Entityframework class that has
  // String Name, String AlternateText, Byte[] ActualImage,
  //  and String ContentType
  PhotoViewImage newImage = new PhotoViewImage();
  HttpPostedFileBase file = Request.Files["OriginalLocation"];
  newImage.Name = photo.Name;
  newImage.Alt = photo.AlternateText;

  //Here's where the ContentType column comes in handy.  By saving
  //  this to the database, it makes it infinitely easier to get it back
  //  later when trying to show the image.
  newImage.ContentType = file.ContentType;

  Int32 length = file.ContentLength;
  //This may seem odd, but the fun part is that if
  //  I didn't have a temp image to read into, I would
  //  get memory issues for some reason.  Something to do
  //  with reading straight into the object's ActualImage property.
  byte[] tempImage = new byte[length];
  file.InputStream.Read(tempImage, 0, length);
  newImage.ActualImage = tempImage ;

  newImage.Save();

  //This part is completely optional.  You could redirect on success
  // or handle errors ect.  Just wanted to keep this simple for the example.
  return View();
}

And here’s the mark up to get this ball a rollin’:

<form method="post" enctype="multipart/form-data" action="Photo/Upload">
  <div>
    <span>
     Name:
   </span>
   <span>
     <input type="text" id="Name" name="Name" />
   </span>
  </div>
  <div>
    <span>
      Alternate Text:
    </span>
    <span>
     <input type="text" id="AlternateText" name="AlternateText" />
    </span>
  </div>
  <div>
    <span>
      Image
    </span>
    <span>
      <input type="file" id="OriginalLocation" name="OriginalLocation" />
    </span>
  </div>
  <div>
    <input type="submit" value="Upload" />
  </div>
</form>

Biggest thing to notice in the markup is the enctype=”multipart/form-data”. This is a must to upload images. It was something I was missing originally and annoyed the hell out of me.

Showing the Image

So now that we have a we to upload the image, how the hell do you use it? Well that’s not too hard. It just involves a new type of result, an action, and an img element.

So the first thing you need is an image result, and in using my superior intellect I came up with such a thing. And by superior intellect I mean I used StackOverflow. Oddly enough though, it’s actually the second post that I got it from and I changed it a little. However, it was very useful.

using System.Web;
using System.Web.Mvc;
using System.IO;

public class ImageResult : ActionResult
{
  public String ContentType { get; set; }
  public byte[] ImageBytes { get; set; }
  public String SourceFilename { get; set; }

  //This is used for times where you have a physical location
  public ImageResult(String sourceFilename, String contentType)
  {
    SourceFilename = sourceFilename;
    ContentType = contentType;
  }

  //This is used for when you have the actual image in byte form
  //  which is more important for this post.
  public ImageResult(byte[] sourceStream, String contentType)
  {
    ImageBytes = sourceStream;
    ContentType = contentType;
  }

  public override void ExecuteResult(ControllerContext context)
  {
    var response = context.HttpContext.Response;
    response.Clear();
    response.Cache.SetCacheability(HttpCacheability.NoCache);
    response.ContentType = ContentType;

    //Check to see if this is done from bytes or physical location
    //  If you're really paranoid you could set a true/false flag in
    //  the constructor.
    if (ImageBytes != null)
    {
      var stream = new MemoryStream(ImageBytes);
      stream.WriteTo(response.OutputStream);
      stream.Dispose();
    }
    else
    {
      response.TransmitFile(SourceFilename);
    }
  }
}

And here’s how you use the actual result.

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult ShowPhoto(Int32 id)
{
  //This is my method for getting the image information
  // including the image byte array from the image column in
  // a database.
  PhotoViewImage image = PhotoViewImage.GetById(id);
  //As you can see the use is stupid simple.  Just get the image bytes and the
  //  saved content type.  See this is where the contentType comes in real handy.
  ImageResult result = new ImageResult(image.ActualImage, image.ContentType);

  return result;
}

And the markup would go a little sumthin’ like dis:

  <img src="/Photo/ShowPhoto/1" alt="" />

And now you too can upload an image to a database, show it, and then decide just to physically host the images anyway. Next post will be about how to use this with jQuery and asynchronously. I bet you can’t wait!

,

  • http://www.cadabrapacific.com Steve

    Ah well done, very cool.
    I am new to MVC and have written much the same to upload the images…I was about to revert to asp.net and create an httpHandler to stream the image out when I thought…someone somewhere must have done this in MVC before!
    Cheers,
    Steve

    • http://www.byatool.com Sean

      Don’t be a cheater. No one likes a cheater… Unless you play Baseball.

  • http://www.isyndica.com Carlos

    Newbie question; where should I create the custom actionresult class? Any specific folder or just inside the controller?

    • http://www.byatool.com Sean

      Do you mean the ImageResult class? If so, that’s really just preference. Personally I wouldn’t have it straight on the controller since it could be used elsewhere in other projects.

      Even if you aren’t going to have other projects, I have a general
      rule of only one class per file for easier lookup and cleaner class files. There might be a chance you will have other result classes too so it wouldn’t hurt to have a ActionResult folder or something like that. The reason I don’t have a namespace to denote that it’s in a seperate area is just to keep the example simple.

      Mind you there are other school of thoughts, mainly people who don’t like folder clutter ie a million class files. They rely more on just namespaces to sort through the mess. Just depends on your style.

  • http://www.vanick.com nathan

    Is caching the image as simple as changing the response's Cache properties in the ExecuteResult method? Surely it can't be that simple!

    • http://www.byatool.com Sean

      I'm not sure it is preventing any specific image from being cached, but more so that no images are being cached.

  • Endy

    Could you send the PhotoViewImage class to me ?

  • Endy

    THX very much

    • http://www.byatool.com Sean

      I will look for it tonight as far as recovering the old project, since it might have been a casualty of the move. The semi finished one is a bit more complex than this post.

      • Endy

        Thanks.

  • Vincent

    Very nice! Thanks a bunch.

  • Bez

    Hi

    is it possible to send my a copy codes for working version. As I am new to MVC need a working example to follow. Thanks

  • http://aspnet.aun.su Asp.Net

    could you upload the source project files?

  • bms

    Why not just use FileResult?
    public ActionResult GetImage()
    {
    PhotoViewImage image = PhotoViewImage.GetById(id);

    return File(image.ActualImage, image.СontentType);
    }

    • http://www.byatool.com Sean

      Wish I could tell you. It’s either that it didn’t work for what I wanted or this post is older than that result. Or I’m dumb. So there’s three possibilities for yo.

  • http://n/a nic

    Hi Sean,
    I like this article, Did you try to display many images in the View? Your sample is a single image display.

    thanks

    • http://www.byatool.com Sean

      Far as I remember, and it’s been a while, I did in fact build a site that multiple images shown in one view. I really wish I could find the example project I had for this up and running.