Image gallery in asp.net mvc with multiple file and size

Image gallery in asp.net mvc or in any other language can be achieved by using different free JavaScript libraries. What we need for a good image gallery, at least we need images in two sizes to show thumbnail and then the original image on clicking the image. In this article we will write a small method to calculate the new image size and save images in multiple sizes. To upload image we will use html input type file control with multiple upload at a time.

Before diving in coding let’s look what we are going to achieve.

  1. Upload multiple image file at a time
  2. Save images in thumbnail size (100 x 100) and bigger of size (600 x 600)
  3. Show image in rows with bootstrap classes
  4. On clicking the thumb image show the bigger image in bootstrap modal popup
  5. Navigate images from modal by using blueimg JavaScript free library
  6. Option to auto play the image
  7. Option to select any image at any time
  8. We will use WebGrid to show the paging and searching options as well

How will it look like the main page on images, here is the screen shot of the page? alt text

To create this look and feel we don’t want much of the coding except some CSS and JavaScript files for which we will use CDN and some to download from GitHub, check how we can set it up. You need to download two files one from CSS and other from JavaScript folder to

  1. Content/bootstrap-image-gallery.min.css
  2. Scripts/bootstrap-image-gallery.min.js

Open the layout view in shared folder and add all the CSS links to header and JavaScript at the bottom of the page

// CSS files
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//blueimp.github.io/Gallery/css/blueimp-gallery.min.css">
<link rel="stylesheet" href="~/Content/bootstrap-image-gallery.min.css">

// JavaScript files
@Scripts.Render("~/bundles/jquery")
<script src="//blueimp.github.io/Gallery/js/jquery.blueimp-gallery.min.js"></script>
<script src="~/Scripts/bootstrap-image-gallery.min.js"></script>

That's it for the layout, we done it.

Create a model to save the image into database, same model we will use to create the database by using the Entity Framework 6, we will save the images in two sizes one for thumbnail and other for large in folder /GalleryImages and /GalleryImages/Thumbs so create them accordingly. Create a model class Photo into Models folder on the root and use following code in it

public class Photo
{
    [Key]
    public int PhotoId { get; set; }

    [Display(Name = "Decription")]
    [Required]
    public String Decription { get; set; }

    [Display(Name = "Image Path")]
    public String ImagePath { get; set; }

    [Display(Name = "Thumb Path")]
    public String ThumbPath { get; set; }


    [Display(Name = "Created On")]
    public DateTime CreatedOn { get; set; }
}

It’s a very simple model with photo id, description, image path, thumbnail path and creation date, create a new folder call DAL to create the data access code with the help of Entity Framework 6, create a new class GalleryContext into this folder (DAL) and add following code to it.

public class GalleryContext : DbContext
{
    public GalleryContext()
        : base("DefaultConnection")
    {
        Database.SetInitializer<GalleryContext>
            (new DropCreateDatabaseIfModelChanges<GalleryContext>());
    }

    public DbSet<Photo> Photos { get; set; }
}

DefaultConnection is a connection string into web.config file, open it and give the name to our database say MVCImageGallery, here is my web.config file connection string section

<connectionStrings>
   <add name="DefaultConnection" 
     connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=MVCImageGallery;
      Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\MVCImageGallery.mdf" 
     providerName="System.Data.SqlClient" />
</connectionStrings>

As you can see we have add the code for searching and paging so to achieve it we need one more model to pass the values to view. In Models folder add a new class PagedList and add following code in it.

public class PagedList<T>
{
    public List<T> Content { get; set; }
    public Int32 CurrentPage { get; set; }
    public Int32 PageSize { get; set; }
    public int TotalRecords { get; set; }
}

Time to create the action for index by using above created models, open HomeController in Controllers folder and remove all the code in it and add Index action with following code

public ActionResult Index(string filter = null, int page = 1, int pageSize = 20)
{
    var records = new PagedList<Photo>();
    ViewBag.filter = filter;

    records.Content = db.Photos
                .Where(x => filter == null || (x.Description.Contains(filter)))
                .OrderByDescending(x=>x.PhotoId)
                .Skip((page - 1) * pageSize)
                .Take(pageSize)
                .ToList();

    // Count
    records.TotalRecords = db.Photos
                    .Where(x => filter == null || (x.Description.Contains(filter))).Count();

    records.CurrentPage = page;
    records.PageSize = pageSize;

    return View(records);
}

I am using paginated data in it, means it will only fetch those much of records which we have to show in our page rather than complete data from database and show only 10 or 20. I use filter to search the record on the basis of entered text in textbox on the main page. I ordered the record by photo id in descending order so it can always show the newly added record and the very first record on the page.

Right click in index action and create a new view, if already create in viewa/home change the code to this one, which I will explain in more detain

@model MVCImageGallery.Models.PagedList<MVCImageGallery.Models.Photo>

@{
    ViewBag.Title = "Image Gallery";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Image Gallery</h2>

@using (Html.BeginForm("index", null, FormMethod.Get))
{
    <div class="row">
        <div class="col-sm-8">
            <div class="input-group">
                <input type="text"
                        name="filter"
                        value="@ViewBag.filter"
                        class="form-control"
                        style="display: inline"
                        placeholder="Search" />
                <span class="input-group-btn">
                    <button class="btn btn-default" type="submit">Go</button>
                </span>
            </div>
        </div>
        <div class="pull-right col-lg-1">
            <a class="btn btn-success" data-modal="" href="/home/create" id="btnCreate">
                <span class="glyphicon glyphicon-plus"></span>
            </a>
        </div>
    </div>


    <!-- The Bootstrap Image Gallery lightbox, should be a child element of the document body -->
    <div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls">
        <!-- The container for the modal slides -->
        <div class="slides"></div>
        <!-- Controls for the borderless lightbox -->
        <h3 class="title"></h3>
        <a class="prev">‹</a>
        <a class="next">›</a>
        <a class="close">×</a>
        <a class="play-pause"></a>
        <ol class="indicator"></ol>
        <!-- The modal dialog, which will be used to wrap the lightbox content -->
        <div class="modal fade">
            <div class="modal-dialog">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" aria-hidden="true">&times;</button>
                        <h4 class="modal-title"></h4>
                    </div>
                    <div class="modal-body next" style="max-height: 500px; min-height: 400px;"></div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-default pull-left prev">
                            <i class="glyphicon glyphicon-chevron-left"></i>
                            Previous
                        </button>
                        <button type="button" class="btn btn-primary next">
                            Next
                        <i class="glyphicon glyphicon-chevron-right"></i>
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div style="margin-top: 17px;">
        <div id="links">
            @foreach (var item in Model.Content)
            {
                <a href="@item.ImagePath" title="@item.Description" data-gallery>
                    <img src="@item.ThumbPath" alt="@item.Description" class="img-rounded" style="margin-bottom:7px;" />
                </a>            
            }
        </div>
    </div>    

    <div>
        @{
            var grid = new WebGrid(
                        canPage: true,
                        rowsPerPage: Model.PageSize,
                        canSort: true,
                        ajaxUpdateContainerId: "grid");

            grid.Bind(Model.Content, rowCount: Model.TotalRecords, autoSortAndPage: false);
            grid.Pager(WebGridPagerModes.All);

            @grid.GetHtml(htmlAttributes: new { id = "grid" },   
            fillEmptyRows: false,
            mode: WebGridPagerModes.All,
            columns: grid.Columns());
        }
    </div>
}

Let's review this code in more detail

  1. Html.BeginForm uses get method to always get the data than to post
  2. First section is to add the search and add button which is simple and straight forward
  3. Second section is to open the model popup on clicking any image in the list
  4. Third section is to render our image in a foreach loop with thumbnail and large image path to open in modal popup
  5. WebGrid: why we need a web grid with no columns? If you want to use the paging you need to write some code or some external library to manage the paging. But I don't want to write any code for it so I used the WebGrid to show the paging and on every page number click get new record and bind it with the help of my foreach loop.

That's it for the main page, now we need a way to add images to our gallery, for it, we are going to create a new action Create in our HomeController

[HttpGet]
public ActionResult Create()
{
    var photo = new Photo();
    return View(photo);
}

Right click on the Create Action and create a new view, if already created then replace the code with this one

@model MVCImageGallery.Models.Photo

@{
    ViewBag.Title = "Upload Images";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Upload Images</h2>
<div class="well">
@using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/form-data" })) {
    @Html.AntiForgeryToken()

    <div class="form-horizontal">

        <div class="form-group">
            @Html.LabelFor(m => Model.Description, new { @class = "control-label col-sm-3" })
            <div class="col-sm-5">
                @Html.TextBoxFor(m => m.Description, new { @class = "form-control required"})                
                @Html.ValidationMessageFor(model => model.Description)
            </div>
        </div>

        <div class="form-group">
             @Html.Label("Choose Image(s)", new { @class = "control-label col-sm-3" })
            <div class="col-sm-5">
                <input type="file" name="files"
                    multiple="multiple" accept=".jpg, .png, .gif"/>
            </div>
        </div>

        <div class="form-group">
            <div class="col-sm-5 col-sm-offset-3">
                <input type="submit" value="Save" class="btn btn-primary" />
                <div style="color:red">
                    @ViewBag.error
                </div>
            </div>
        </div>
    </div>
}
    </div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

BegingForm use uses FormMethod.Post and added new html attributes to post the files (images) as well to do so we added new { enctype = "multipart/form-data" } in it. To upload the file we use input type file with multiple file selection and upload, final output of the page

alt text

Before writing the post action method I would Like to create two method one to calculate the new size of the image,

public Size NewImageSize(Size imageSize, Size newSize)
{
    Size finalSize;
    double tempval;
    if (imageSize.Height > newSize.Height || imageSize.Width > newSize.Width)
    {
        if (imageSize.Height > imageSize.Width)
            tempval = newSize.Height / (imageSize.Height * 1.0);
        else
            tempval = newSize.Width / (imageSize.Width * 1.0);

        finalSize = new Size((int)(tempval * imageSize.Width), (int)(tempval * imageSize.Height));
    }
    else
        finalSize = imageSize; // image is already small size

    return finalSize;
}

I don't think we need explanation of this simple code, it check the image width and height, which is bigger, take that an calculate the ratio into tempval variable and multiply it with image width and height to calculate the new image size without losing the image quality.

Method to save the image into our created folder

private void SaveToFolder(Image img, string fileName,  string extension, Size newSize, string pathToSave)
{
    // Get new resolution
    Size imgSize = NewImageSize(img.Size, newSize);
    using (System.Drawing.Image newImg = new Bitmap(img, imgSize.Width, imgSize.Height))
    {
        newImg.Save(Server.MapPath(pathToSave), img.RawFormat);
    }
}

Add the post action method to save the data and images

[HttpPost]
public ActionResult Create(Photo photo, IEnumerable<HttpPostedFileBase> files)
{
    if (!ModelState.IsValid)
        return View(photo);
    if (files.Count() == 0 || files.FirstOrDefault() == null)
    {
        ViewBag.error = "Please choose a file";
        return View(photo);
    }

    var model = new Photo();
    foreach (var file in files)
    {
        if (file.ContentLength == 0) continue;

        model.Description = photo.Description;
        var fileName = Guid.NewGuid().ToString();
        var extension = System.IO.Path.GetExtension(file.FileName).ToLower();

        using (var img = System.Drawing.Image.FromStream(file.InputStream))
        {
            model.ThumbPath = String.Format("/GalleryImages/thumbs/{0}{1}" , fileName, extension);
            model.ImagePath = String.Format("/GalleryImages/{0}{1}", fileName, extension);

            // Save thumbnail size image, 100 x 100
            SaveToFolder(img, fileName, extension, new Size(100, 100),  model.ThumbPath);

            // Save large size image, 800 x 800
            SaveToFolder(img, fileName, extension, new Size(600, 600), model.ImagePath);                  
        }

        // Save record to database
        model.CreatedOn = DateTime.Now;
        db.Photos.Add(model);
        db.SaveChanges();
    }

    return RedirectPermanent("/home");
}

There is nothing complicated to explain in this part. I use GUID to create the new file name and thumbnail image. First i save both the images into folder and save those paths and description into database.

Once you will try to upload more files, immediately you will get the error, Maximum request length exceeded. so how to fix it? Add following lines to our web.config file, for more detail see Stackoverflow

<configuration>
    <system.web>
        <httpRuntime maxRequestLength="1048576" />
    </system.web>
</configuration>

For IIS7 and above, you also need to add the lines below:

 <system.webServer>
   <security>
      <requestFiltering>
         <requestLimits maxAllowedContentLength="1073741824" />
      </requestFiltering>
   </security>
 </system.webServer>

Here is the large image view in modal popup on clicking the image in gallery alt text

See the demo

Ali Adravi Having 13+ years of experience in Microsoft Technologies (C#, ASP.Net, MVC and SQL Server). Worked with Metaoption LLC, for more than 9 years and still with the same company. Always ready to learn new technologies and tricks.
  • mvc
  • c#
  • image
  • image-gallery
  • asp.net
By Ali Adravi On 10 Jan, 15  Viewed: 35,852

Other blogs you may like

mvc search page example with code

MVC Searh page with pagination: It’s very easy to create a search page in asp.net but when I try to create the same in MVC I faced many problems, how to create model, how to keep searched values in search controls, pagination, I found myself nowhere, so start searching for some good examples but... By Ali Adravi   On 25 Aug 2013  Viewed: 40,270

MVC insert update delete and select records

CRUD (Create, Retrieve, Update and Delete) in MVC. When we start to learn new language, first we try to run an application with “Hello World” and then CRUD functionality. So in this article we will see how to select records from database (with WebGrid, pagination and sort functionality), update a... By Ali Adravi   On 17 Aug 2013  Viewed: 106,155

How to create a single thanks page for entire controller in MVC

Sometimes we need a thanks page say we have user registration, change password, activate account functionality in our application then we need a thanks page after registering with our site, to say thanks for registering with us or showing confirmation that your password is successfully changed or... By Hamden   On 30 Jun 2013  Viewed: 3,794

MVC jquery autocomplete with value and text field

In MVC, autocomplete with jquery is the best way to pull data from database and render according to our requirements, so in this article we will use jquery to show the auto complete text box without any ajax controls. We need a method to get the data from database, a controller method to handle the... By Ali Adravi   On 29 Jun 2013  Viewed: 7,051

Upload files with model data in MVC

Upload multiple files with model data in MVC is really very easy, when I started to test by uploading some files, I though it would be more complicated but it is really not. In my previous post [ASP.Net MVC file upload][1], I promised to post soon about how to upload multiple files. When I was... By Ali Adravi   On 04 Jun 2013  Viewed: 25,607