Uploading photos with MVC

By James at December 22, 2010 13:49
Filed Under:

There are many articles online talking about MVC, but not many showing how to upload files. In a project I’m working on I need to have my users upload photographs and wanted them to be able to preview the photo before submitting it. Here’s a screen shot of what I’m talking about.

7318.image_677BC867

The application allows the user to select a photo, which is then previewed. The user can then add a caption and description of the photo. During this first round the Save button is bound to the following jQuery script:

   1: function uploadMemberPhoto(elem) {
   2:     var $elem = $(elem);    
   3:     $("div.memberPhotoUploader").fadeIn(500);
   4:     var $form = $elem.parents("form");
   5:     $form.attr("action", "/MemberPhotos/Uploaded");
   6:     $elem.fadeOut(500, function () { $form[0].submit(); });
   7: }

which sets the action of the form to “/MemberPhotos/Uploaded”, then submits the form. The Controller action “Uploaded” handles the processing of the image.

   1: public ActionResult Uploaded(HttpPostedFileBase file, FormCollection formCollection)
   2: {
   3:   var memberPhoto = _photoHelpers.UploadMemberPhoto(file, formCollection);  
   4:   return View("UploadedPhoto");
   5: }

_photoHelpers is a helper class which does the processing of the image

   1: public MemberPhoto UploadMemberPhoto(HttpPostedFileBase file, FormCollection collection)
   2: {
   3:   var memberPhoto = new MemberPhoto();
   4:   try
   5:   {
   6:      if(file.ContentLength > 0)
   7:      {
   8:       var member = _repo.Get<Member>(int.Parse(collection["memberId"]));
   9:       var photoCaption = collection["memberPhotoCaption"];
  10:       var photoDescription = collection["memberPhotoDescription"];                    
  11:       var serverPath = HttpContext.Current.Server.MapPath(
  12:              ConfigurationManager.AppSettings["memberAvatarPath"]);
  13:       var memberFolder = GetMemberImageFolder(member);
  14:       var imagePath = serverPath + memberFolder;
  15:  
  16:       if (!Directory.Exists(imagePath))
  17:           Directory.CreateDirectory(imagePath);
  18:  
  19:       bool success = ImageHelpers.ProcessMemberPhotoUpload(file, imagePath);
  20:       if(success) // insert the record
  21:       {
  22:           memberPhoto.MemberId = member.Id;
  23:           memberPhoto.PhotoUrl = file.FileName;
  24:           memberPhoto.PhotoCaption = photoCaption;
  25:           memberPhoto.PhotoDescription = photoDescription;
  26:           memberPhoto.IsApproved = false;
  27:           memberPhoto.IsVisible = true;
  28:           memberPhoto.CreateBy = member.Email;
  29:           memberPhoto.CreateDate = DateTime.Now;
  30:           memberPhoto.ModifyBy = member.Email;
  31:           memberPhoto.ModifyDate = DateTime.Now;
  32:           memberPhoto.Mode = ImageHelpers.ImageMode(file);
  33:           _repo.Create(memberPhoto);
  34:        }
  35:     }
  36:  }
  37:   catch (Exception ex)
  38:   {
  39:     memberPhoto = null;
  40:   }
  41:   return memberPhoto;
  42: }
   1: public bool ProcessMemberPhotoUpload(HttpPostedFileBase img, string fPath)
   2: {
   3:   fPath = fPath.Replace("\\\\", "\\");
   4:   var resizedImage = ResizeImage(img, new Size(640, 480));
   5:   var status = SaveImage(resizedImage, fPath, img.FileName);
   6:  
   7:   var thumbNail = ResizeImage(img, new Size(102, 77));
   8:   status = SaveImage(thumbNail, fPath, "thmb_" + img.FileName);
   9:  
  10:   var largeThumb = ResizeImage(img, new Size(400, 600));
  11:   status = SaveImage(largeThumb, fPath, "lgthmb_" + img.FileName);
  12:   return status;
  13: }

The rest of the methods used in the helper class are here

   1: private static Image ResizeImage(HttpPostedFileBase img, Size size)
   2: {
   3:   var oImage = Image.FromStream(img.InputStream);
   4:   var sWidth = oImage.Width;
   5:   var sHeight = oImage.Height;
   6:             
   7:   if(size.Width >= oImage.Width && size.Height >= oImage.Height)
   8:       return (Image) oImage;
   9:  
  10:   float nPercent = 0;
  11:   float nPercentW = 0;
  12:   float nPercentH = 0;
  13:  
  14:   nPercentW = ((float) size.Width/(float) sWidth);
  15:   nPercentH = ((float) size.Height/(float) sHeight);
  16:  
  17:   if (nPercentH < nPercentW)
  18:      nPercent = nPercentH;
  19:   else
  20:      nPercent = nPercentW;
  21:  
  22:   int dWidth = (int) (sWidth*nPercent);
  23:   int dHeight = (int) (sHeight*nPercent);
  24:  
  25:   Bitmap b = new Bitmap(dWidth, dHeight);
  26:   Graphics g = Graphics.FromImage((Image) b);
  27:   g.InterpolationMode = InterpolationMode.HighQualityBicubic;
  28:   g.DrawImage(oImage, 0, 0, dWidth, dHeight);
  29:   g.Dispose();
  30:   return (Image) b;
  31:  }
  32:  
  33:  private static bool SaveImage(Image img, string savePath, string fileName)
  34:  {
  35:    var status = true;
  36:    try
  37:    {
  38:       var newPath = new StringBuilder(savePath).Append(fileName);
  39:       File.Delete(newPath.ToString());
  40:       img.Save(newPath.ToString(), img.RawFormat);
  41:    }
  42:    catch (Exception)
  43:    {
  44:      status = false;
  45:    }
  46:    return status;
  47: }
  48:  
  49: public string ImageMode(HttpPostedFileBase img)
  50: {
  51:   var oImage = Image.FromStream(img.InputStream);
  52:   var mode = "landscape";
  53:   if (oImage.Width < oImage.Height) mode = "portrait";
  54:      oImage.Dispose();
  55:   return mode;
  56: }

In this particular application, all photos uploaded need to be approved, so after the photo is uploaded, the page refreshes, keeping the original form action of “/MemberPhotos/UpdatePhoto”

8688.image_61FD31F6

   1: public ActionResult UpdatePhoto(MemberPhoto photo)
   2: {
   3:    photo.ModifyBy = User.Identity.Name;
   4:    photo.ModifyDate = DateTime.Now;
   5:    _repo.Edit(photo);
   6: }

You may be wondering how the image is previewed. I found this article at, http: //weblogs.asp.net/imranbaloch/archive/2010/04/03/image-preview-in-asp-net-mvc.aspx and modified it a bit for my needs. This bit of javascript works like a champ.

   1: function ChangeImage(fileId, imageId) {
   2:     $("div.memberPhotoUploader").fadeIn(500);
   3:     $("#form1").ajaxSubmit({ success: function (responseText) {
   4:         if (isImage(responseText)) {
   5:             var d = new Date();
   6:             $(imageId)[0].src = "/ImagePreview/ImageLoad?a=" + d.getTime();
   7:             $(imageId).show();
   8:             $("input#uploadButton").removeClass("btnSaveDisabled")
   9:               .addClass("btnSaveEnabled").removeAttr("disabled").click(function () { 
  10:                 uploadMemberPhoto(this); 
  11:           }); ;
  12:         } else {
  13:             $(imageId).hide();
  14:             $("div.imageTypeNotice").show();
  15:         }
  16:         $("div.memberPhotoUploader").fadeOut(500);
  17:     }
  18:     });
  19: }

Happy programming from ComponentOne

James

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


About the author

James James is a five time and current Microsoft MVP in Client App Development, a Telerik Insider, a past Director on the INETA North America Board, a husband and dad, and has been developing software since the early days of Laser Discs and HyperCard stacks. As the Founder and President of the Inland Empire .NET User's Group, he has fondly watched it grow from a twice-a-month, early Saturday morning group of five in 2003, to a robust and rambunctious gathering of all types and sizes of .NET developers.

James loves to dig deep into the latest cutting edge technologies - sometimes with spectacular disasters - and spread the word about the latest and greatest bits, getting people excited about developing web sites and applications on the .NET platform, and using the best tools for the job. He tries to blog as often as he can, but usually gets distracted by EF, LINQ, MVC, ASP, SQL, XML, and most other types of acronyms. To keep calm James plays a mean Djembe and tries to practice his violin. You can follow him on twitter at @latringo.

And as usual, the comments, suggestions, writings and rants are my own, and really shouldn't reflect the opinions of my employer. That is, unless it really does.

James Twitter Feed

Recent Comments

Comment RSS

Month List