ساخت یک گالری تصویر مبتنی بر دیتابیس در MVC

چهارشنبه 18 مرداد 1396

در این مقاله نحوه ساخت یک گالری تصویر زیبا و پنل مدیریت این گالری را خواهیم آموخت. در آموزش، تصاویر را به صورت باینری در دیتابیس ذخیره می کنیم و سپس برای نمایش به کاربر، تصاویر را از دیتابیس دریافت می نماییم.

ساخت یک گالری تصویر مبتنی بر دیتابیس در MVC

در این مقاله، موارد زیر توضیح داده خواهد شد.

-ذخیره تصویر در دیتابیس

-دریافت تصویر از دیتابیس

-ساخت تصاویر بندانگشتی از تصاویر داخل دیتابیس

-آپلود تصویر به کمک Ajax

-گالری تصاویر MVC

-گالری FancyBox

ساخت دیتابیس

ابتدا یک دیتابیس با نام دلخواه ایجاد می کنیم و سپس دو جدول (طبق کدهای زیر) به دیتابیس اضافه می نماییم.

    CREATE TABLE [dbo].[WebFiles](  
        [Id] [int] IDENTITY(1,1) NOT NULL,  
        [Data] [varbinary](max) NULL,  
        [IsActive] [bit] NOT NULL,  
        [UpdateDate] [datetime] NOT NULL,  
        [FileName] [nvarchar](max) NULL,  
        [FileExt] [nvarchar](max) NULL,  
        [FileLength] [int] NOT NULL,  
        [ContentType] [nvarchar](max) NULL   
     CONSTRAINT [PK_WebFiles] PRIMARY KEY CLUSTERED   
    (  
        [Id] ASC  
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]  
      
      
    CREATE TABLE [dbo].[Gallery](  
        [Id] [int] IDENTITY(1,1) NOT NULL,  
        [Title] [nvarchar](max) NULL,  
        [WebImageId] [int] NOT NULL,  
        [IsActive] [bit] NOT NULL,  
        [OrderNo] [int] NULL,  
     CONSTRAINT [PK_Gallery] PRIMARY KEY CLUSTERED   
    (  
        [Id] ASC  
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]  
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]   

فایل ها به صورت باینری در جدول WebFiles ذخیره می شود و اطلاعات فایل ها به صورت جداگانه در جدول Gallery  نگه داری می شوند. شما می توانید به جدول Gallery ستون های مورد نظرتان را اضافه کنید.

ساخت برنامه MVC

یک برنامه MVC بدون احراز هویت (no authentication) می سازیم.

افزودن فایل EDMX 

ما در اینجا از Entity Framework  و رویکرد Database-First استفاده می کنیم. پس یک فایل edmx بسازید و هر دو جدول  دیتابیس را به برنامه اضافه کنید. نتیجه باید به شکل زیر باشد.

ویرایش صفحه layout.cshtml

صفحه پیشفرض layout.cshtml را ویرایش می کنیم، در این صفحه باید لینک صفحات gallery admin page و Gallery را اضافه نماییم.

    <div class="navbar-collapse collapse">  
                    <ul class="nav navbar-nav">  
                        <li>@Html.ActionLink("Home", "Index", "Home")</li>  
                        <li>@Html.ActionLink("Upload", "Index", "GalleryAdmin")</li>  
                        <li>@Html.ActionLink("Gallery", "Index", "Gallery")</li>  
                    </ul>  
                </div>   

آپلود تصویر (Gallery Admin)

برای آپلود تصاویر به کمک AJAX و ذخیره آنها در دیتابیس، باید یک کنترلر جدید بسازیم. پس یک کنترلر با نام GalleryAdminController به برنامه اضافه می نماییم.

اگر اکشن متد Index در کنترلر موجود نبود، این اکشن را اضافه کنید.

    public ActionResult Index()  
    {  
          return View();  
    }  

ساخت View برای اکشن Index

    @{  
        ViewBag.Title = "Gallery Admin";  
    }  
      
    <h2>Gallery Admin</h2>  
    <div id="ImagePanel" data-url="@Url.Action("Create")">  
        @Html.Action("Create")  
    </div>  
    <div class="row">  
        <div class="col-md-offset-2 col-md-10">  
            <div class="alert alert-danger" style="display:none" role="alert" id="errormessage"></div>  
            <div class="alert alert-success" style="display:none" role="alert" id="successmessage"></div>  
        </div>  
    </div>  
    <hr />  
    <div id="ListPanel" data-url="@Url.Action("_List")">  
        @Html.Action("_List")  
    </div>  

در اینجا ما به دو partial view برای متدهای Create و List نیاز داریم که بعد از افزودن عکس، بتوانیم این partial view ها را به راحتی رفرش کنیم. یک اکشن با نوع HttpGet برای نمایش create می سازیم. بخاطر داشته باشید که در پایان این اکشن ها خروجی باید از نوع PartialView باشد.

public ActionResult Create()  
{  
  ImageEditorViewModel vm = new ImageEditorViewModel();   
  return PartialView(vm);  
} 

برای استفاده از ViewModel، یک کلاس جدید با نام ImageEditorViewModel می سازیم.

    public class ImageEditorViewModel  
    {  
     public int Id { get; set; }  
     [Required]  
     public string Caption { get; set; }  
      
     [Required]           
     public HttpPostedFileBase FileImage { get; set; }  
      
     internal static Gallery getEnityModel(ImageEditorViewModel model)  
     {  
       return new Gallery  
       {  
         IsActive = true,  
         Title = model.Caption,   
         OrderNo = 0,  
       };  
     }   
    }  

حالا باید یک View برای create با نام Create.cshtml بسازیم. حتما پس از ساخت این View، فایل های jQuery validation را به این صفحه بیافزایید تا عمل اعتبارسنجی در سمت client به درستی انجام شود.

    @model ImageGallery.ViewModel.ImageEditorViewModel  
      
    @using (Html.BeginForm("Create", "GalleryAdmin", FormMethod.Post, new { enctype = "multipart/form-data", Id="frmAddImage" }))  
    {  
        <div class="form-horizontal">  
            <h4>Add Image Here</h4>  
            <hr />  
            @Html.ValidationSummary(true, "", new { @class = "text-danger" })  
            <div class="form-group">  
                @Html.LabelFor(model => model.Caption, htmlAttributes: new { @class = "control-label col-md-2" })  
                <div class="col-md-10">  
                    @Html.EditorFor(model => model.Caption, new { htmlAttributes = new { @class = "form-control" } })  
                    @Html.ValidationMessageFor(model => model.Caption, "", new { @class = "text-danger" })  
                </div>  
            </div>  
      
      
            <div class="form-group">  
                <div class="col-md-2">  
                    <label class="pull-right">  Image:</label>  
                </div>  
                <div class="col-md-9">  
                    @Html.TextBoxFor(m => m.FileImage, new { type = "file" })  
                    @Html.ValidationMessageFor(model => model.FileImage, "", new { @class = "text-danger" })  
                </div>  
            </div>   
      
            <div class="form-group">  
                <div class="col-md-offset-2 col-md-10">  
                    <input type="submit" id="btnAddImage" value="Create" class="btn btn-primary" />  
                </div>  
                
            </div>   
        </div>  
    }  
    @Scripts.Render("~/bundles/jqueryval")  

کدهای بالا ظاهر View را ایجاد می نماید، حالا با افزودن کدهای جاوااسکریپت زیر به create.cshtml، قابلیت ارسال اطلاعات به صورت Ajax، به برنامه اضافه می شود.

    <script type="text/javascript">  
      
        $(function () {  
            $("#btnAddImage").on("click", function (e) {  
                var $form = $("#frmAddImage");  
                $form.validate();  
                if (!$form.valid()) {  
                    return false;  
                }  
      
                e.preventDefault();  
                var formData = new FormData();  
                var data = $form.find(':input[name]:not(:disabled)').filter(':not(:checkbox), :checked').map(function () {  
                    var input = $(this);  
      
                    if (input.attr('type') == "file") {  
      
                        formData.append(input.attr('name'), input[0].files[0]);  
                        return {  
                            name: input.attr('name'),  
                            value: input[0].files[0]  
                        };  
                    } else {  
                        formData.append(input.attr('name'), input.val());  
                        return {  
                            name: input.attr('name'),  
                            value: input.val()  
                        };  
                    }  
                }).get();  
      
                $.ajax({  
                    url: '/GalleryAdmin/Create',  
                    data: formData,  
                    type: "POST",  
                    contentType: false,  
                    processData: false,  
                    success: function (data, textStatus, jqXHR) {   
                        if (data.success == true) {  
                            $("#successmessage").html(data.Caption + " has been added successfully").show().delay(5000).fadeOut();  
                            $("#ListPanel").load($("#ListPanel").data("url"));  
                            $("#ImagePanel").load($("#ImagePanel").data("url"));  
                            return true;  
                        }  
                        if (data.ExceptionMessage) {  
                            $("#errormessage").html(data.ExceptionMessage).show().delay(5000).fadeOut();  
                            return false;  
                        }  
                        if (data.ValidationMessage) {  
                            $("#errormessage").html(data.ExceptionMessage).show().delay(5000).fadeOut();  
                            return false;  
                        }  
                        return false;  
                    },  
                    error: function (jqXHR, textStatus, errorThrown) {  
                        $("#errormessage").html(textStatus).show().delay(5000).fadeOut();  
                    },  
                    complete: function (jqXHR, textStatus) {  
                    }  
                });  
      
            });  
        });  
    </script>  

در اینجا، ابتدا فرم را اعتبارسنجی می نماییم و سپس تمامی کنترل ها و ورودی های فایل را در شی formdata اضافه می نماییم. سپس اطلاعات را با یک درخواست Post به روش Ajax به سمت سرور ارسال می نماییم. در صورت موفقیت آمیز بودن این درخواست، partial view های create و list را رفرش می نماییم. اگر خطایی رخ دهد، این خطا را در صفحه نمایش خواهیم داد.

اکنون وقت ساخت یک اکشن متد در داخل GalleryAdminController برای ذخیره داده های دریافت شده در دیتابیس است.

    [HttpPost]  
    public ActionResult Create(ImageEditorViewModel model)  
    {  
      try  
      {  
         if (ModelState.IsValid)  
         {  
            ImageGalleryEntities db = new ImageGalleryEntities();   
            var fileModel = WebFileViewModel.getEntityModel(model.FileImage);  
            db.WebFiles.Add(fileModel);  
            db.SaveChanges();  
     
            var entity = ImageEditorViewModel.getEnityModel(model);                    
            entity.WebImageId = fileModel.Id;  
            entity.OrderNo = db.Galleries.Count() > 0 ? db.Galleries.Max(x=> x.OrderNo) + 1 : 1;  
            db.Galleries.Add(entity);  
            db.SaveChanges();  
      
            return Json(new { success = true, Caption = model.Caption });  
           }  
      
           return Json(new { success = false, ValidationMessage = "Please check validation messages" });  
         }  
         catch (Exception ex)  
         {  
            return Json(new { success = false, ExceptionMessage = "Some error here" });  
          }  
      }  
    }

کدهای بالا، باعث ذخیره شدن رکورد های شما در جدول های Gallery و WebFiles می شود. حالا باید یک ViewModel برای webfile بسازیم و اطلاعات فایل های فرستاده شده را به صورت WebFileViewModel دریافت نماییم.

    public class WebFileViewModel  
    {  
           public static WebFile getEntityModel(HttpPostedFileBase file)  
           {  
               WebFile webfile = new WebFile();  
               MemoryStream target = new MemoryStream();  
               file.InputStream.CopyTo(target);  
               byte[] data = target.ToArray();  
               webfile.Data = data;  
               webfile.ContentType = file.ContentType;  
               webfile.FileExt = Path.GetExtension(file.FileName);   
               webfile.FileLength = file.ContentLength;  
               webfile.FileName = file.FileName;  
               webfile.IsActive = true;  
               webfile.UpdateDate = DateTime.Now;  
               return webfile;  
           }  
    }  

حالا باید یک لیست از تصاویر برای نمایش در صفحه ادمین، بسازیم برای اینکار یک اکشن متد با نام _List و مطابق قطعه کد زیر، می سازیم.

    public ActionResult _List()  
    {  
       ImageGalleryEntities db = new ImageGalleryEntities();  
       var list = db.Galleries.OrderBy(x => x.OrderNo)  
                            .Select(x => new ImageList  
                            {  
                                Id = x.Id,  
                                IsActive = x.IsActive,  
                                OrderNo = x.OrderNo,  
                                WebImageId = x.WebImageId,  
                                Title = x.Title  
                            }).ToList();  
      
        return PartialView(list);  
    }  

سپس یک View با نام _List.cshtml می سازیم و کدهای زیر را در آن وارد می نماییم.

    @model IEnumerable<ImageGallery.ViewModel.ImageList>  
      
    <ul style="list-style:none;" class="row">  
        @foreach (var item in Model)  
        {  
            <li class="col-md-3">  
                <br />  
                <img class="img" src="@Url.Action("GetImage", "DownloadFile", new { Area ="" , Id= item.WebImageId, t =2 })" 
                 style="width:100%;height:180px;" />  
                <br />  
                <label>@item.Title</label>  
                <br />  
                <br />  
            </li>  
        }  
    </ul>  

در اینجا ما پارامتر t را برابر با 2 قرار داده ایم تا تصاویر بندانگشتی با ضریب 2 ساخته شوند.

صفحه ادمین کامل شد و تمامی کارهای لازم انجام شد، اکنون می توانید برنامه را اجرا کنید و صفحه ایی مانند تصویر زیر مشاهده نمایید.

دانلود تصاویر

یک کنترلر جدید با نام DownloadFileController به برنامه اضافه می کنیم و اکشن متد زیر را به این کنترلر اضافه می کنیم.

    [OutputCache(Duration = 60, Location = OutputCacheLocation.Any, VaryByParam = "Id;t")]
    public ActionResult GetImage(int Id = 0, int t = 0)
    {
       if (Id == 0)
       {
          return HttpNotFound();
       }
       ImageGalleryEntities db = new ImageGalleryEntities();
       var model = db.WebFiles.Find(Id);
       if (model != null)
       {
          if (t != 0)
          {
             byte[] img = getThumbNail(model.Data, t);
             return File(img, model.ContentType, "thumb_" + model.FileName);
          }
          return File(model.Data, model.ContentType, model.FileName);
       }
       else
       {
          return HttpNotFound();
       }
    }

در این اکشن متد، ما دو پارامتر دریافتی قرار داده ایم، پارامتر اول برای Id تصویر و پارامتر دوم برای سایز تصویر است. در صورتیکه فایل مورد نظر در دیتابیس پیدا نشود یا اصلا کاربر Id را وارد نکرده باشد، خطای not found را ارسال می نماییم. شما می توانید به جای این کار، کاربر به یک صفحه خطای 404 منتقل نمایید.

اگر پارامتر t را دریافت نکنیم، تصویر را در حالت full size به کاربر نمایش خواهیم داد و اگر پارامتر t را دریافت نماییم، یک تصویر بندانگشتی با ضریب t را به کاربر نمایش می دهیم. حالا وقت افزودن متد getThumbNail به کنترلر DownloadFile است.

    private byte[] getThumbNail(byte[] data, int multi = 1)
    {
       using (var file = new MemoryStream(data))
       {
          int width = 200 * multi;
          using (var image = Image.FromStream(file, true, true)) /* Creates Image from specified data stream */
          {
             int X = image.Width;
             int Y = image.Height;
             int height = (int)((width * Y) / X);
             using (var thumb = image.GetThumbnailImage(width, height, () => false, IntPtr.Zero))
             {
                var jpgInfo = ImageCodecInfo.GetImageEncoders()
                               .Where(codecInfo => codecInfo.MimeType == "image/png").First();
                using (var encParams = new EncoderParameters(1))
                {
                   using (var samllfile = new MemoryStream())
                   {
                      long quality = 100;
                      encParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);
                      thumb.Save(samllfile, jpgInfo, encParams);
                      return samllfile.ToArray();
                   }
                };
             };
          };
        };
    } 

ساخت گالری

این بخش ساده تر از بخش قبلی است. فقط کافی است که لیست تصاویر را نمایش دهیم و گالری fancy box را به برنامه اضافه نماییم. برای این کار یک کنترلر جدید با نام GalleryController  می سازیم و دو اکشن متد index و _list را به این کنترلر اضافه می کنیم.

    public ActionResult Index()  
    {  
        return View();  
    }  
      
    public ActionResult _List()  
    {  
        ImageGalleryEntities db = new ImageGalleryEntities();  
        var list = db.Galleries.OrderBy(x => x.OrderNo)  
                            .Select(x => new ImageList  
                            {  
                                Id = x.Id,  
                                IsActive = x.IsActive,  
                                OrderNo = x.OrderNo,  
                                WebImageId = x.WebImageId,  
                                Title = x.Title  
                            }).ToList();  
      
        return PartialView(list);  
    }  

سپس index.cshtml را می سازیم.

    <h2>Gallery</h2>  
    <hr />  
    <link href="~/Scripts/fancy-box/jquery.fancybox.min.css" rel="stylesheet" />  
      
    @Html.Action("_List")  
      
    <script src="~/Scripts/fancy-box/jquery.fancybox.min.js"></script>  

شما باید fancybox 3 را دانلود کنید و مرجع های فایل های css و js  این گالری را در بالای کدهای برنامه تان اضافه نمایید. در پروژه نمونه، این گالری وجود دارد.

سپس فایل _List.cshtml را می سازیم.

    @model IEnumerable<ImageGallery.ViewModel.ImageList>  
      
    <ul style="list-style:none;" class="row">  
        @foreach (var item in Model)  
        {  
            <li class="col-md-3">  
                <br />  
                <a data-fancybox="gallery" data-caption="@item.Title" 
                   href="@Url.Action("GetImage", "DownloadFile", new { Area ="" , Id= item.WebImageId })"> 
                   <img class="img" src="@Url.Action("GetImage", "DownloadFile", new { Area ="" , Id= item.WebImageId, t =2 })" 
                   style="width:100%;height:180px;" />  
                </a>  
                <br />  
                <label>@item.Title</label>  
                <br />  
                <br />  
            </li>  
        }  
    </ul>  

گالری تصاویر آماده است و نتیجه آن شبیه تصویر زیر است. 

آموزش asp.net mvc

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید