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!