[MVC 3] Image Preview–JQuery


UPDATE: I have now combined all my MVC Image Handling  blog posts into a Open Source Project. Feel free to check this out once you have read the post.

http://mvcimage.codeplex.com/

Original Blog Post:

At the moment I’m learning ASP.NET and MVC 3. Lets say I’m loving it at the moment, and at this present moment, I’m trying to employ my new skills in creating a new site for my friends friend, but i cannot disclose anymore on that….for now.

What i can disclose is snippets and code samples on my journey. Once such snippet is Image Previewing. Sounds easy, but as I’m new to this, it isn’t!

What I’m blogging about today and code I’m showing you is based on lots of articles i have read on the web. I’m one of those coders that cannot just Google + Copy & Paste, then move on to the next problem (to be resolved by the latter). I need to try and understand what the code is doing. So in the last week i have learnt:

  • JQuery and some JavaScript
  • A little more on how HTTP works behind the scenes using an awesome tool called Fiddler.
  • Specific browser requirements

Now before i move on, i have based my code on the following. Before this i was learning JQuery, so i wanted to amend the JavaScript portion to JQuery, but keeping the same concept:

http://weblogs.asp.net/imranbaloch/archive/2010/04/03/image-preview-in-asp-net-mvc.aspx

In addition my good friend and fellow Web Developer, Shaw Young, helped me with some tweaks! So thank you Imran for teaching me and Shaw for helping me!

Before i start discussing the JQuery, the following is the HTML that the code references.

        <input class="upload" type="file" name="imageLoad2" id="File1"/> <img alt="Preview" id="imgThumbnail2" height="26px" width="26px" /> 

So lets start from the beginning. I have created a JQuery script file, which is bound to the change JavaScript event, using the JQuery handler .change().

$(document).ready(function () {
  var inputTag = ".upload";
  var imgTag = "#imgThumbnail2";
  $(inputTag).change(function () {

I’m presuming you understand JQuery, but the .change() is hooking ANY input tags with the CSS class name of .upload and referencing any image tags with the id of  #imgThumbnail2 (which if you are using id, should be 1!).

Next we create a cloned image tag. We do this, because modern browsers will not allow you to copy the filename/directory values from one tag to another due to security reasons.  We then insert this cloned tag after the original .upload one.

NB: If you query the value in IE9 of the directory, you will be greeted with C:\Fakepath… now.

//-- Create a input attribute with the same type as the original //-- Insert it after the original's location. $('<input>').attr(
{
           class: "upload-clone",
           type: $(inputTag).attr('type'),
           name: "name-clone" }).insertAfter(inputTag);

Next we need to create a hidden form which we will post to one of our action methods Ajaxsubmit.

//--Create a hidden form with an action method pointer to //--our custom controller. $("<form>").attr(
{
           method: "post",
           id: "prototype",
           action: "/ImagePreview/AjaxSubmit" }).appendTo("body").hide();

We then need to change our new form’s encoding. Because different browsers behave differently, we change the attribute using the following.

//--Change the encoding based on the browser, as IE doesn't allow you to//--change the encoding. //--Append our original input to the hidden form. $('#prototype').attr((this.encoding ? 'encoding' : 'enctype'), 'multipart/form-data');

So we can submit the form and its contents, in this case the .upload input tag, we append it to the form, and hide it.

$(inputTag).appendTo("#prototype").hide();

We then use AJAX to submit the input .upload and its contents. Because we set the action method ImagePreview/Ajaxsubmit, this will be called, then the image tag will use the ImageLoad method. The first set is the JQuery and the 2nd is to re-iterate Imran’s method.

JQuery:

//--Use AJAX to post the form, and if successful, load the binary info the //--image tag. $('#prototype').ajaxSubmit(
        {
            success: function (responseText) {
                var d = new Date();

                $(imgTag)[0].src = "/ImagePreview/ImageLoad?a=" +                  d.getMilliseconds();

                $(inputTag).insertAfter('.upload-clone').show();

                $('.upload-clone').remove();
                $('#prototype').remove();

            }

        });

C# Action Methods:

public class ImagePreviewController : Controller {
    #region Ajax Submit
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult AjaxSubmit(int? id)
    {
        Session["ContentLength"] = Request.Files[0].ContentLength;
        Session["ContentType"] = Request.Files[0].ContentType;
        byte[] b = new byte[Request.Files[0].ContentLength];
        Request.Files[0].InputStream.Read(b, 0, Request.Files[0].ContentLength);
        Session["ContentStream"] = b;
        return Content(Request.Files[0].ContentType + ";" +         Request.Files[0].ContentLength);
    }
    #endregion #region ImageLoad
    public ActionResult ImageLoad(int? id)
    {
        byte[] b = (byte[])Session["ContentStream"];
        int length = (int)Session["ContentLength"];
        string type = (string)Session["ContentType"];
        Response.Buffer = true;
        Response.Charset = "";
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
        Response.ContentType = type;
        Response.BinaryWrite(b);
        Response.Flush();
        Response.End();
        Session["ContentLength"] = null;
        Session["ContentType"] = null;
        Session["ContentStream"] = null;
        return Content("");
    }
    #endregion }

So that’s it, with a little of JQuery magic, sprinkled with MVC goodness, we have an Image Previewing client side code, mixed with some AJAX.

I will try and get a solution up for people to use ASAP. In the meantime, if anyone needs it, let me know and i will email them the code.
Next up i will be:

  1. Adding some client side validation to make sure we are selecting image content types.
  2. Wiring up the JQuery to a HTML Helper using Razor for input file types.
  3. Create a T4 scaffolding template for input types to be re-used.