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:
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!