ASP.NET MVC 5 Image Upload & Delete w/ Calculated Property

Share httpJunkie.com on..
Tweet about this on TwitterShare on FacebookShare on Google+Share on TumblrEmail this to someone

So I have had the need for an implementation of Uploading and Deleting a single image for a product on a few of my client websites where the requirements call for no more than one image for each product. My solution is creating a calculated property within my POCO class that strips the spaces out of the Product Name and appends “.Jpg” to the end to create a file name for the product image. This means you don’t have to store the URL in a database and you don’t have to store the image in a database. You simply use a calculated property inside the POCO class to crate a name for the image and call it when you loop through the products wherever you like, just as if it were a url string from the database. I will demonstrate how this is done.

I don’t claim this to be a great solution, but rather an easy to implement solution for a small project on a budget.

Below is my POCO class for a fictional website I have created for Demo purposes called Tasty Cakes.

~/Models/Cakes.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TastyCakes.Models
{
    public class Cakes
    {
        public int CakesID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }

        public string CakeImage
        {
            get { return Name.Replace(" ", string.Empty) + ".jpg"; }
        }
    }
}

Easy enough. You could in fact create multiple images if you wanted, but I don’t think this is the most scalable solution so whenever I use it, it’s mostly on a budget website on a small scale.

From this POCO class I have created a default Entity Framework controller but I have added both an UploadPhoto and a DeletePhoto Action. I have abbreviated the default controller code to get to the point where it matters, but still show the structure of the controller, here it is:

~/Controllers/CakesController.cs

using System;
using System.IO;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;
using TastyCakes.Models;

namespace TastyCakes.Controllers
{
    public class CakesController : Controller
    {
        private ApplicationDbContext db = new ApplicationDbContext();

        // GET: /Cakes/
        public ActionResult Index()
        {
            return View(db.Cakes.ToList());
        }

        // GET: /Product-5
        public ActionResult Details(int? id)
        {
            ...
        }

        // GET: /Cakes/Create
        public ActionResult Create()
        {
            return View();
        }

        // POST: /Cakes/Create
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create([Bind(
        Include="CakesID,Name,Description,Price")] Cakes cakes)
        {
            ...
        }

        // GET: /Cakes/Edit/5
        public ActionResult Edit(int? id)
        {
            ...
        }

        // POST: /Cakes/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(
        Include="CakesID,Name,Description,Price")] Cakes cakes)
        {
           ...
        }

        // GET: /Cakes/Delete/5
        public ActionResult Delete(int? id)
        {
            ...
        }

        // POST: /Cakes/Delete/5
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            ...
        }

        [HttpPost]
        public ActionResult UploadPhoto(string cakesImage,
        HttpPostedFileBase photo)
        {
            string path = @"C:\YourProjectFolders\TastyCakes\Images\Cakes\"
            + cakesImage;

            if (photo != null)
                photo.SaveAs(path);

            return RedirectToAction("Index");
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult DeletePhoto(string photoFileName)
        {
            //Session["DeleteSuccess"] = "No";
            var photoName = "";
                photoName = photoFileName;
                string fullPath = Request.MapPath("~/Images/Cakes/"
                + photoName);

                if (System.IO.File.Exists(fullPath))
                {
                    System.IO.File.Delete(fullPath);
                    //Session["DeleteSuccess"] = "Yes";
                }
                return RedirectToAction("Index");
        }

        protected override void Dispose(bool disposing)
        {
            ...
        }
    }
}

And finally the Edit page of the Cakes CRUD operations (Some code has been shifted to the line below to prevent scrolling in the code block:

~/Views/Cakes/Edit.cshtml

@model TastyCakes.Models.Cakes</pre>
<div class="row">
<div class="large-12 columns"><hgroup class="title">
<h1>Edit Cakes</h1>
</hgroup>
 @using (Html.BeginForm())
 {
 @Html.AntiForgeryToken()
<div class="form-horizontal">

<hr />

 @Html.ValidationSummary(true)
 @Html.HiddenFor(model => model.CakesID)
<div class="medium-12 column">@Html.LabelFor(model => model.Name)
 @Html.EditorFor(model => model.Name)
 @Html.ValidationMessageFor(model => model.Name)</div>
<div class="medium-12 column">@Html.LabelFor(model => model.Description)
 @Html.EditorFor(model => model.Description)
 @Html.ValidationMessageFor(model => model.Description)</div>
<div class="medium-12 column">@Html.LabelFor(model => model.Price)
 @Html.EditorFor(model => model.Price)
 @Html.ValidationMessageFor(model => model.Price)</div>
<div class="medium-12 column"><input class="tiny button" type="submit" value="Save" />
 @Html.ActionLink("Back to List", "Index", null,
 new { @class = "tiny button" })</div>
</div>
 }

 @if (File.Exists(Server.MapPath("~/Images/Cakes/"
 + Html.DisplayFor(modelItem => Model.CakeImage))))
 {
 <img alt="" src="~/Images/Cakes/@Html.DisplayFor(modelItem   => Model.CakeImage)" />
<form action="/Cakes/DeletePhoto" method="post" name="deletePhoto">@Html.AntiForgeryToken()

If the image above is not the correct image,
 hit delete and Re-Upload:

 <input type="text" name="photoFileName" value="@Html.DisplayFor(modelItem          => Model.CakeImage)" />

 <input class="tiny button" type="submit" value="Delete" /></form>
 }
 else
 {
<form action="/Cakes/UploadPhoto" enctype="multipart/form-data" method="post"><label for="photo">Upload a Photo:</label>
 <input class="tiny button" id="photo" type="file" name="photo" />
 <input class="tiny button" type="submit" value="Upload" />
 <input type="hidden" name="cakesImage" value="@Html.DisplayFor(modelItem   => Model.CakeImage)" /></form>
 }

 @section Scripts {
 @Scripts.Render("~/bundles/jqueryval")
 }</div>
</div>
<pre>

It’s pretty self explanatory. You just need to make sure your Image folder is targeted correctly and everything should work fine. The user should not be able to post multiple images to the folder because of the logic in the view. Like I said, not the most scalable solution. But it works for me! Great for demo purposes when you cannot connect to a database ro for brevity! Try it out and as always, hit me up on twitter at @httpJunkie if you have any comments or questions.

Leave a Reply

You must be logged in to post a comment.