[MVC 3] Images – Downloading Images


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:

The previous posts i have shown you how to upload images, and use JQuery to preview the image. We have also used Entity Framework 4.0 and SQL to save a image as a varbinary data type.

Now I’m going to show you how to download the image, and display it on your view, using a HTML Helper.

Before we start lets think about what we need:

  • HTML Helper Extension to help display the image
  • Controller classes to grab the image
  • Object validation methods

Lets start with the code for the HTML Helper Extension. You should be familiar with how these work now. Again, mine is to accept a model using Lambda expressions:

public static MvcHtmlString DisplayImageFor<TModel, TProperty>    (this HtmlHelper<TModel> helper,
    Expression<Func<TModel, TProperty>> expression, string alt = null,     string action = null string controller= null, string actionParameterName = null,string height = null, string width = null)
{

    if (String.IsNullOrEmpty(alt)){
        string _name = ExpressionHelper.GetExpressionText(expression);
        alt = helper.ViewContext.ViewData.TemplateInfo.        GetFullHtmlFieldName(_name);
    }

    if (String.IsNullOrEmpty(height)){
        height = "126px";
    }

    if (String.IsNullOrEmpty(width)){
        width = "126px";
    }

    if(String.IsNullOrEmpty(actionParameterName)){
        actionParameterName = "id";
    }

    ///---Set the default src settings if null 
 ///--- src element is made up of action, controller and acionParameterName 
 if (String.IsNullOrEmpty(action)){
        action = "GetImage";

    }

    if (String.IsNullOrEmpty(controller)){
        controller = "ImagePreview";
    }

    ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression,     helper.ViewData);
    Object value = metadata.Model;
    Type valueType = metadata.Model.GetType();
string src = null;

    if(ObjectValidation.IsStringType(valueType))
    {
        src = String.Format(CultureInfo.InvariantCulture,
        "/{0}/{1}/{2}",controller,action,value);
    }

    var imgBuilder = new TagBuilder("img");

    imgBuilder.MergeAttribute("alt", alt);
    imgBuilder.MergeAttribute("src", src);
    imgBuilder.MergeAttribute("height", height);
    imgBuilder.MergeAttribute("width", width);

    return MvcHtmlString.Create(imgBuilder.ToString(TagRenderMode.SelfClosing));

}

So in a nutshell:

  1. If the parameters are null we set up the default values for the HTML Elements.
  2. build the metadata from the ViewData dictionary. We can then load the metadata.Model data to an Object, i.e. the value of the model.
  3. We build the src element to include the link of the controller we will build later to get the image.
  4. We use again to build the HTML Elements.
  5. The alt element will end up being the models name if there isn’t a parameter passed, for e.g.
     1: <img width="126" height="126" alt="property.UniqueKey" src="/ImagePreview/GetImage/b8b03b6d-e30c-46d6-9cba-002f3a4699ee"/>

     

You will notice we have used a static method, ObjectValidation.IsStringType. This has been built to allow me to query the object type. I need this so i can make sure the parameter I’m passing to the controller is a String. Here is how i have implemented this:

public static bool IsStringType(Type type)
{
    if (type == null)
    {
        return false;
    }

    switch (Type.GetTypeCode(type))
    {
        case TypeCode.String:
            return true;

        case TypeCode.Object:
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return IsStringType(Nullable.GetUnderlyingType(type));
            }

            return false;

    }

    return false;
}

Quite  a simple class, which passes the type of the object (Previously we used .GetType() to achieve this), then we test to see if the equals String. If it does, we return true.

The other check is if the object is generic and Nullable, we pass the underlying type back through to the IsStringType method.

We have now built our HTML Helper and our Object Validation method, now onto our GetImage action method we set in the src element.

        #region GetImage
        public ActionResult GetImage(Guid id)
        {
            //Guid ID = new Guid(id); Medium profileimage = new Medium();

            try {
                int count = db.Media.Count(c => c.Unique_Key == id);

                if(count > 0)
                {
                    profileimage =  db.Media.SingleOrDefault(i => i.Unique_Key == id);
                    //--Convert the Image data into Memory. 
 byte[] imagedata = profileimage.Source;
                    return File(imagedata, profileimage.Content_Type, profileimage.File_Name);

                }

                count = 0;
                count = db.Media.Count(c => c.Table_Link == id);

                if (count > 0)
                {
                    profileimage = db.Media.SingleOrDefault(i => i.Table_Link == id);
                    //--Convert the Image data into Memory. 
 byte[] imagedata = profileimage.Source;
                    return File(imagedata, profileimage.Content_Type, profileimage.File_Name);
                }

                return File(new byte[0], null);
            }
            finally {
                if (db != null)
                    db.Dispose();

            }

        }

        #endregion 

This controller method contains alot of my own bespoke business logic in obtaining the image. Which is why i do 2 checks.

Enhanced by Zemanta

[MVC 3] Image Preview –HTML Helpers


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:

In my i showed you how to preview an image using a combination of JQuery/AJAX and controller code behind.

This blog post i will show you how to take that a step further and take the HTML code and generate a nice HTML Helper. HTML Helpers are a nice way of re-using your code for future projects.

Lets take a look at the HTML code we wish to generate on the fly:

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

This is the code that our JQuery, plays around with to preview an image. Not a lot of code, but wouldn’t be nice to encapsulate this within an Helper?

Firstly what are helpers? Well all they are is code that returns HTML either as a string or Mvchtmlstring. They are useful where you have a lot of conditional logic, plus they also allow you to pass through your model as LINQ Lambda Expression.  Most of the common ones are scaffolded when you create a CRUD controller, that creates your Controller and View using the default T4 Templates.

image

Ones like:

  1. Html.EditorFor()
  2. Html.LabelFor()
  3. Html.DropDownListFor()

There are lots of them, check them out using your Razor view.

image

So, to create my custom  helpers, I’ve created a folder in my Project called Helpers and a static class ImageHelpers . Because we are creating a HTML Helper that works like the ones included in the framework, we need to create them using . So you will notice the first parameter is thisHtmlHelper<TModel> helper which will define this extension method as an extension of HtmlHelper. The rest of the parameters allow us to customise the method if needed.

We also have created the method to allow us to pass the generic model and property types. This is useful so we can pass any of our models through, and later on we will add a new T4 template which includes varbinary in the templating. That is a different post though.

 public static class ImageHelpers {
     public static MvcHtmlString UploadImageFor<TModel, TProperty>
       (this HtmlHelper<TModel> helper,
       Expression<Func<TModel, TProperty>> expression, string id = null,
       string name = null, string cssclass = null, string alt = null,
       string imgId = null, string height = null, string width = null)
     {
         const string type = "file";

         if (cssclass == null)
         {
             cssclass = "upload";
         }

         if (id == null)
         {
             id = "File1";
         }

         if (name == null)
         {
             name = "imageLoad2";
         }

         if (alt == null)
         {
             alt = "Preview";
         }

         if (imgId == null)
         {
             imgId = "imgThumbnail2";
         }

         if (height == null)
         {
             height = "126px";
         }

         if (width == null)
         {
             width = "126px";
         }

         var fileBuilder = new TagBuilder("input");
         var imgBuilder = new TagBuilder("img");
         var mergedBuilder = new TagBuilder("p");

         ///Firstly we build the input tag. //--Add the CSS class. fileBuilder.AddCssClass(cssclass);        
        //--Add the name. fileBuilder.MergeAttribute("name", name);        
        //--Add the id. fileBuilder.MergeAttribute("id",id);        
        //--Add the type. fileBuilder.MergeAttribute("type",type);

        ///Secondly we build the img tag. //--Add the alt. imgBuilder.MergeAttribute("alt", alt);        
        //--Add the id. imgBuilder.MergeAttribute("id", imgId);        
        //--Add the height and width. imgBuilder.MergeAttribute("height", height);        
        imgBuilder.MergeAttribute("width", width);

         ///Merge the two together into a p tag. 
        mergedBuilder.AddCssClass("file-upload-");        
        mergedBuilder.InnerHtml += fileBuilder;        
        mergedBuilder.InnerHtml += imgBuilder;        
        return MvcHtmlString.Create(mergedBuilder.ToString());

     }

}

So, we use three variables to to build an input, image and paragraph tags. This is a nifty little class that allows us to build HTML Elements, and some methods and properties to allow us to quickly to get our desired result, and we return a once we have built it.

We set the default elements to be the names we use in our JQuery, so that the default logic is applied if we have our jquery-previewimage.js used.

We then add our Html helper to our view (After deleting the original code):

          <div class="editor-label"> @Html.LabelFor(model => model.ProfileImage)         
         </div> <div class="editor-field"> @Html.UploadImageFor(model => model.ProfileImage)         
        </div> 

This will generate the original code as seen here when run:

image

So there we have it, our own HTML Helper which we can re-use for future projects.

[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.