بهینه سازی تصاویر ( کاهش حجم و اندازه تصاویر ) در ASP.NET MVC

می دانیم که بهینه سازی تصاویر و کاهش حجم آن ها از جمله کار هایی است که باعث افزایش سرعت سایت می شود. همچنین افزایش سرعت سایت از موارد سئو به حساب می آید و باعث افزایش رتبه سایت خواهد شد. پس فشرده سازی تصویر و کاهش حجم آن در هر سایتی حتما باید انجام شود.
ما قصد داریم در این مقاله روش هایی را برای بهینه سازی تصاویر، فشرده سازی تصاویر در ASP.NET MVC در اختیار شما قرار دهیم.
یکی از مهم ترین اعمال بعد از طراحی سایت، آزمایش عملکرد آن سایت به خصوص سرعت آن می باشد. GTMetrix.com پیشنهاد خوبی برای انجام این کار است. این سایت این امکان را به شما می دهد که سرعت سایت خود را سنجیده و اشکال کار خود را برطرف کنید. یکی از ایراداتی که باعث کاهش سرعت سایت می شود، قرار دادن تصاویر با حجم بالا می باشد.
از دلایلی که می تواند باعث افزایش حجم تصاویر باشد، تعداد بایت های انتقالی به مرورگر در هربار انتقال است. اگر تعداد بایت ها زیاد باشد باعث کاهش سرعت سایت خواهد شد. برای رفع این مشکل باید قبل از انتقال تصاویر به مرورگر، به بهینه سازی تصاویر و کاهش حجم آن ها بپردازید. این راه باعث می شود که شما تعداد بایت های کمتری را به مرورگر انتقال دهید و کارایی سایت افزایش پیدا کند.

راه های فشرده سازی تصاویر در ASP.NET MVC

1) قبل از آپلود، از یک دستور برای کاهش حجم و بهینه سازی تصاویر استفاده کرده و آنها را در یک پوشه کوچک (thumbnail) ذخیره کنید.
2) در صورت درخواست کاربر ، تصاویر را به صورت پویا و داینامیک کوچک کنید و به تغییر اندازه سایز داینامیک تصاویر بپردازید.
بهتر است برای این که حجم زیادی از فضای هاست برای ذخیره تصاویر استفاده نشود و برای کاهش حجم آن، از راه دوم استفاده کنیم.

در صورت وجود درخواست، برای انجام روش دوم از کنترلر استفاده می کنیم.
در تگ تصویر(image tag) به یک اکشن در کنترلر اشاره می کنیم(Url.Action) که کار تغییر اندازه سایز داینامیک تصاویر را برای ما انجام می دهد و نتیجه را برمی گرداند.
برای این کار به نام فایل در تگ ImageResult و در داخل ActionResult نیاز داریم.
به سراغ اکشن کنترلر می رویم:

public ActionResult Thumbnail(string filename) {
	var img = new WebImage(filename).Resize(291, 285, false, true);
	return new ImageResult(new MemoryStream(img.GetBytes()), "binary/octet-stream");
}

برای هر صفحه ای که خواستید از این عمل استفاده کنید، یک کنتلر بسازید و در آن اکشن، دستورات خود را قرار دهید.
همچنین به ابعاد تصاویر در تگ تصویر نیز نیاز داریم که در حال حاضر آن را در این قسمت قرار دادیم ولی بعدا آن را باید داخل فایل css قرار دهیم.
علاوه بر آن به یک کلاس WebHelper که WebImage نیز نامیده می شود احتیاج داریم.
WebHelper اصول تغییر اندازه سایز داینامیک تصاویر را فراهم می کند. با اینکه ما فقط از امکان تغییر اندازه اش استفاده کردیم، اما امکانات دیگری نیز دارد مانند:
1) می توانید یک عکس یا متن واتر مارک به تصاویر اضافه کنید.
2) می توانید شبیه سازی یا clone کنید.
3) امکان برش دادن یا crop کردن را برای شما فراهم می کند.
4) امکان چرخش عمودی و افقی و یا چپ و راست را هم فراهم می کند.

سپس به سراغ اکشن ImageResult می رویم.
ImageResult  یکActionResult  ساده می باشد که یک جریان باینری هشتایی را (binary/octet-stream) بر می گرداند.

public class ImageResult: ActionResult {
  public ImageResult(Stream imageStream, string contentType) {
    if (imageStream == null) throw new ArgumentNullException("imageStream");
    if (contentType == null) throw new ArgumentNullException("contentType");
    this.ImageStream = imageStream;
    this.ContentType = contentType;
  }
  public Stream ImageStream {
    get;
    set;
  }
  public string ContentType {
    get;
    set;
  }
  public override void ExecuteResult(ControllerContext context) {
    if (context == null) throw new ArgumentNullException("context");
    var response = context.HttpContext.Response;
    response.ContentType = this.ContentType;
    byte[] buffer = new byte[4096];
    while (true) {
      int read = this.ImageStream.Read(buffer, 0, buffer.Length);
      if (read == 0) break;
      response.OutputStream.Write(buffer, 0, read);
    }
    response.End();
  }
}

برای بهینه سازی تصاویر سایت بهترین راه استفاده از جریانی از بایت ها برای انتقال می باشد. بعضی افراد تصاویر را در حافظه ذخیره می کنند و سپس آن را ارسال می کنند. اما استفاده از outputstream راهی سریع تر برای ارائه تصاویر فراهم می کند.

نحوه ی عملکرد سینتکس های razor چگونه است؟
سینتکس Razor ممکن است کمی عجیب به نظر برسد ، اما راهی سریع و برای تغییر اندازه سایز داینامیک تصاویر می باشد.

< img src = "@Url.Action("Thumbnail ", "Home ", new {FileName="~/Content/Images/Cop.jpg "})"
    class = "img-responsive"
    title = "@post.Title"
    alt = "@post.Title"
    width = "291"
    height = "285" / >

با razor ما مجبور نیستیم ابعاد یک تصویر را برای مرورگر تعیین  کنیم . اگر ابعاد تصاویر را تعیین نکنیم مرورگر منتظر می ماند تا تصویر به صورت کامل دانلود شود.
با این روش شما با استفاده از کلاس ImageResult و یک کلاس WebImage به کاهش حجم و فشرده سازی تصویر می توانید بپردازید.

راه دیگر برای بهینه سازی تصاویر،کاهش حجم و تغییر اندازه تصاویر در ASP.NET MVC استفاده از Image Resizerمی باشد.
روش های زیادی برای تغییر اندازه سایز داینامیک تصاویر در ASP.NET وجود دارد مانند GDI + ،WPF ، WCI و WebImage که در بالا گفته شد. اکنون می خواهیم به معرفی یک روش ساده دیگر بپردازیم.
ImageResizer دارای یک رابط برنامه نویسی نرم افزار بسیار ساده و قدرتمند است و برای فشرده سازی تصویر ها و بهینه سازی تصاویر و کاهش حجم آن ها از آن استفاده می شود.
به این خاطر که این API منبع باز است ، می توانید از Git بارگیری اش کنید.
همچنین یک کتابخانه پردازش تصویر بهینه و ایمن، برای استفاده در سمت سرور می باشد.

برای استفاده از این روش ابتدا یک برنامه MVC ایجاد کرده و در آن تصویر را بارگذاری می کنیم. در مرحله اول ما یک مدل نمایشگر (ProfileViewModel) را به عنوان قطعه کد زیر ایجاد می کنیم که برای انتقال داده های تصاویر از یک دایرکتوری در کنترلر به نمایشگر استفاده می شود.

using System.Collections.Generic;
namespace EFOperation.Models
{
	public class ProfileViewModel {
		public string ProfileImage {
			get;
			set;
		}
		public FileInfo[] FileInfoes {
			get;
			set;
		}
	}
}

سپس یک کنترلر ایجاد می کنیم که دارای یک اکشن از نوع درخواست GET است که نمایشگر را با داده های تصویر با قطعه کد زیر ارائه می دهد.

using EFOperation.Models;
using ImageResizer;
using System.IO;
using System.Web;
using System.Web.Mvc;
namespace EFOperation.Controllers {
	public class ProfileController: Controller { [HttpGet]
		public ActionResult Index() {
			ProfileViewModel model = newProfileViewModel();
			model.FileInfoes = newDirectoryInfo(Server.MapPath("~/images")).GetFiles();
			return View(model);
		}
	}
}

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

@model EFOperation.Models.ProfileViewModel    
    
<div class="row">    
    <div class="col-lg-6">    
        @using (Html.BeginForm("Index", "Profile", FormMethod.Post, new { enctype = "multipart/form-data", @class = "form-horizontal", role = "form" }))    
        {    
    
            <h4>Upload Image</h4>    
            <hr/>    
            <div class="form-group">    
                @Html.LabelFor(m =>m.ProfileImage, new { @class = "col-md-2 control-label" })    
    
                    <div class="col-md-10">    
                        <input type="file"name="profileFile"id="profileFile"/>    
                    </div>    
            </div>    
            <div class="form-group">    
                <div class="col-lg-4">    
                    <a href="#"class="thumbnail">    
                        <img id="uploading"src=""alt="uploading image">    
                        </a>    
                    </div>    
                </div>    
                <div class="form-group">    
                    <div class="col-md-offset-2 col-md-10">    
                        <input type="submit"class="btnbtn-default" value="Submit"/>    
                    </div>    
                </div>    
        }    
    
    </div>    
        <div class="col-lg-6">    
            <h4>Uploaded Images</h4>    
            <hr/>    
            <div class="row">    
                @foreach (var image inModel.FileInfoes)    
                {    
    
                    <div class="col-lg-3">    
                        <img src="~/images/@image.Name"alt="@image.Name"class="img-circle">    
                        </div>    
                }    
    
                </div>    
            </div>    
        </div>    
    
@section Scripts{    
    @Scripts.Render("~/Scripts/profile-index.js")    
}    

سپس تابع JavaScript را می نویسیم تا در زمان انتخاب تصویر از قطعه کد زیر، یک تصویر بارگذاری شده نشان داده شود.

(function ($) {  
  
    function ProfileIndex() {  
        var $this = this;  
  
        function intialize() {  
            $("#profileFile").change(function () {  
                readURL(this);  
            });  
        }  
  
        function readURL(input) {  
            if (input.files && input.files[0]) {  
                var reader = new FileReader();  
  
                reader.onload = function (e) {  
                    $('#uploading').attr('src', e.target.result);  
                }  
                reader.readAsDataURL(input.files[0]);  
            }  
        }  
  
        $this.init = function () {  
            intialize();  
        }  
    }  
  
    $(function () {  
        var self = new ProfileIndex();  
        self.init();  
    })  
  
})(jQuery)  

اکنون برای ارسال فرم بالا اقدام به ایجاد پست کنید تا تصویر بتواند در فهرست بارگذاری شود. قطعه کد زیر برای عملیات گفته شده می باشد.

[HttpPost]    
public ActionResult Index(HttpPostedFileBaseprofileFile)    
{    
    if (profileFile != null)    
    {    
        string pic = System.IO.Path.GetFileName(profileFile.FileName);    
        string path = System.IO.Path.Combine(Server.MapPath("~/images"), pic);    
        profileFile.SaveAs(path);    
    }    
    return RedirectToAction("Index");    
}    

اکنون می توانید برنامه را اجرا کرده و نتیجه را ببینید.

می بینیم که تصاویر بارگذاری شده در اندازه مناسب نمایش داده نمی شوند تا تصاویر بتوانند در صفحه جای بگیرند ، به همین دلیل به تغییر اندازه سایز داینامیک تصاویر باید بپردازیم.
دو گزینه برای رفع این مشکل رابط کاربر وجود دارد:
1) تغییر اندازه تصویر در زمان بارگذاری و ارائه در رابط کاربری.
2) تغییر اندازه تصویر در زمان بارگذاری و آپلود.
هر دو گزینه را می توان با پلاگین ImageResizer پیاده سازی کرد.
به سراغ گزینه اول می رویم. بسته نرم افزاری “ImageResizer” را در برنامه MVC خود با استفاده از پنجره Manage NuGet Packages (همانطور که در شکل زیر نشان داده شده) نصب می کنیم.

نصب هر دو فایل پیکربندی packages.config و Web.config را به روز می کند.
کد زیر یک پیکربندی معمولی و پایه در پرونده web.config برای این بسته نرم افزاری است.

<configSections>     
   <sectionnamesectionname="resizer"type="ImageResizer.ResizerSection,ImageResizer"requirePermission="false" />    
</configSections>    
    
<modules>    
   <removenameremovename="FormsAuthenticationModule" />    
   <addnameaddname="ImageResizingModule"type="ImageResizer.InterceptModule"/>    
</modules>    

این قسمت از کد برای تنظیم و پیکربندی بسته است. برای به دست آوردن تصاویر در اندازه مناسب، بخشی از نمایشگر را تغییر می دهیم. ما ارتفاع و عرض را به عنوان یک رشته درخواست (query ) به آدرس تصویر منتقل می کنیم و براساس پارامتر های موجود تصویر پردازش می شود. قطعه کد زیر تغییر یافته ی کد نمایشگر بالا می باشد.

<div class="col-lg-6">    
    <h4>Uploaded Images</h4>    
    <hr/>    
    <div class="row">    
        @foreach (var image inModel.FileInfoes) {    
            <div class="col-lg-3">    
                <img src="~/images/@image.Name?w=160&h=100" alt="@image.Name" class="img-circle">    
            </div>    
        }    
    </div>    
</div>    

بعد از اجرا متوجه می شویم که مشکل پیش آمده در بالا حل شده و تصویر در اندازه دلخواه نمایش داده می شود.

این بخش از مقاله گزینه دوم را توضیح می دهد که در آن تصویر در زمان بارگذاری با استفاده از بسته API تغییر اندازه می دهد. ما باید اکشن POST را که در آن اندازه تصویر با استفاده از روش های تغییر اندازه API تعریف می شود، دوباره تعریف کنیم. قطعه کد زیر عملکرد اکشن POST در کنترلر است.

[HttpPost]    
public ActionResult Index(HttpPostedFileBaseprofileFile)    
{    
    if (profileFile != null)    
    {    
        string pic = System.IO.Path.GetFileName(profileFile.FileName);    
        string path = System.IO.Path.Combine(Server.MapPath("~/images"), pic);    
        profileFile.SaveAs(path);    
        ResizeSettings resizeSetting = new ResizeSettings    
        {    
            Width = 150,    
                Height = 100,    
                Format = "png"    
        };    
        ImageBuilder.Current.Build(path, path, resizeSetting);    
    }    
    return RedirectToAction("Index");    
}    

در کد بالا ، ما با استفاده از API مدیریت شده ، تصویری را بارگذاری کرده و تصویر آپلود شده را تغییر اندازه می دهیم. این قطعه کد سه پارامتر را در بر می گیرد که دو پارامتر اول منبع تصویر و مقصدی که می خواهیم تصویر در آن قرار بگیرد را نشان می دهد.
منبع و مقصد تصویر ممکن است یک مسیر فیزیکی (C: ..) ، یک مسیر مجازی مربوط به برنامه (~/folder/image.jpg) ، یا انواع دیگر باشد.

به غیر از روش بالا (اکشن post)، روش ساده تری هم وجود دارد که در کد زیر آمده است.


new ResizeSettings("maxwidth=100&maxheight=100");    
//or    
new ResizeSettings(Request.QueryString);    
//or    
var r = newResizeSettings();    
r.MaxWidth = 100;    
r.MaxHeight = 100; 

اکنون با خیال راحت برنامه را اجرا کرده و تصویری را بارگذاری کنید.