How to dynamically resize images

2

"How to dynamically resize images" by CMSN Software Tutorials, shows you how to resize and cache images dynamically. The logic is to pass the image size as parameters and based on the parameters we generate a dynamic image. Dynamic image will be cache on client browser, server level and application level.

Following sample show you how to dynamically resize the images.  We are using asp.net Generic Web handler for dynamically resize the images. In the asp.net Generic Web handler file we are reading the parameters passed in to the image file and based on the parameters  dynamic image will generated. You don't need to call handler file directly, it can be mapped to the image file extension in the web.config.
//-----------------------------------------------------------------------
// <copyright file="DynamicImage.ashx.cs" company="CMSN Software">
//    Copyright © 2011  CMSN Software
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see http://www.gnu.org/licenses.
// </copyright>
//-----------------------------------------------------------------------
namespace CMSN.Software.Tutorials.HowToDynamicallyResizeImages
{
    using System;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Web;
    using System.Web.Caching;
 
    /// <summary>
    /// Resize images dynamically.
    /// </summary>
    public class DynamicImage : IHttpHandler
    {
        /// <summary>
        /// Default cache duration
        /// </summary>
        private static readonly TimeSpan CacheDuration = TimeSpan.FromDays(30);
 
        /// <summary>
        /// Gets a value indicating whether another request can use the 
        /// <see cref="T:System.Web.IHttpHandler"/> instance.
        /// </summary>
        /// <returns>
        /// true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false.
        /// </returns>
        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the 
        /// <see cref="T:System.Web.IHttpHandler"/> interface.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the 
        /// intrinsic server objects (for example, Request, Response, Session, and Server) 
        /// used to service HTTP requests.
        /// </param>
        public void ProcessRequest(HttpContext context)
        {
            string cacheKeyName = context.Request.Url.PathAndQuery;
            string imagePath = context.Server.MapPath(context.Request.Url.LocalPath);
            string imageExtention = Path.GetExtension(imagePath);
            string contentType = string.Empty;
            byte[] imageFileContent;
            ImageFormat imageFormat = null;
 
            switch (imageExtention)
            {
                case ".png":
                    imageFormat = ImageFormat.Png;
                    contentType = "image/png";
                    break;
                case ".jpg":
                case ".jpeg":
                case ".jpe":
                    imageFormat = ImageFormat.Jpeg;
                    contentType = "image/jpeg";
                    break;
                case ".bmp":
                    imageFormat = ImageFormat.Bmp;
                    contentType = "image/bmp";
                    break;
                case ".gif":
                    imageFormat = ImageFormat.Gif;
                    contentType = "image/gif";
                    break;
                default:
                    break;
            }
 
            context.Response.ContentType = contentType;
 
            if (context.Cache[CacheKey(cacheKeyName)] != null)
            {
                imageFileContent = context.Cache[CacheKey(cacheKeyName)] as byte[];
            }
            else
            {
                int imageWidth = 0;
                int imageHeight = 0;
 
                if (!string.IsNullOrEmpty(context.Request["w"]))
                {
                    if (!int.TryParse(context.Request["w"], out imageWidth))
                    {
                        imageWidth = 0;
                    } 
                }
 
                if (!string.IsNullOrEmpty(context.Request["h"]))
                {
                    if (!int.TryParse(context.Request["h"], out imageHeight))
                    {
                        imageHeight = 0;
                    }
                }
 
                Image originalImage;
 
                if (File.Exists(imagePath))
                {
                    originalImage = Image.FromFile(imagePath);
                }
                else
                {
                    originalImage = new Bitmap(100, 100);
                }
 
                if (imageWidth > 0 || imageHeight > 0)
                {
                    if (imageHeight == 0 && imageWidth > 0)
                    {
                        imageHeight = originalImage.Height * imageWidth / originalImage.Width;
                    }
 
                    if (imageWidth == 0 && imageHeight > 0)
                    {
                        imageWidth = originalImage.Width * imageHeight / originalImage.Height;
                    }
                }
                else
                {
                    imageHeight = originalImage.Height;
                    imageWidth = originalImage.Width;
                }
 
                using (Bitmap newImage = new Bitmap(originalImage, imageWidth, imageHeight))
                {
                    Graphics generatedImage = Graphics.FromImage(newImage);
                    generatedImage.InterpolationMode = InterpolationMode.HighQualityBicubic;
                    generatedImage.SmoothingMode = SmoothingMode.AntiAlias;
                    generatedImage.CompositingQuality = CompositingQuality.HighQuality;
                    generatedImage.DrawImage(originalImage, 0, 0, newImage.Width, newImage.Height);
 
                    // make a memory stream to work with the image bytes
                    using (MemoryStream imageStream = new MemoryStream())
                    {
                        // put the image into the memory stream
                        newImage.Save(imageStream, imageFormat);
 
                        // make byte array the same size as the image
                        byte[] imageContent = new byte[imageStream.Length];
 
                        // rewind the memory stream
                        imageStream.Position = 0;
 
                        // load the byte array with the image
                        imageStream.Read(imageContent, 0, (int)imageStream.Length);
 
                        // return byte array to caller with image type
                        imageFileContent = imageContent;
 
                        using (CacheDependency dependency = new CacheDependency(imagePath))
                        {
                            context.Cache.Insert(
                            CacheKey(cacheKeyName),
                            imageContent,
                            dependency,
                            System.Web.Caching.Cache.NoAbsoluteExpiration,
                            CacheDuration);
                        }
                    }
                }
 
                originalImage.Dispose();
            }
 
            SetResponseCache(context.Response, new string[1] { imagePath });
            context.Response.BinaryWrite(imageFileContent);
        }
 
        /// <summary>
        /// Generate unique Cache key.
        /// </summary>
        /// <param name="key">The cache key.</param>
        /// <returns>Generated unique Cache key</returns>
        protected static string CacheKey(string key)
        {
            return "DynamicImage." + key;
        }
 
        /// <summary>
        /// Sets the response cache.
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="files">The files.</param>
        protected static void SetResponseCache(HttpResponse response, string[] files)
        {
            response.AddFileDependencies(files);
            HttpCachePolicy browserCache = response.Cache;
            DateTime modifiedTime = DateTime.Now;
            browserCache.SetCacheability(HttpCacheability.ServerAndPrivate);
            browserCache.VaryByParams["w"] = true;
            browserCache.VaryByParams["h"] = true;
            browserCache.VaryByParams["v"] = true;
            browserCache.SetOmitVaryStar(true);
            browserCache.SetExpires(modifiedTime.AddDays(7));
            browserCache.SetValidUntilExpires(true);
            browserCache.SetLastModified(modifiedTime);
            browserCache.SetETagFromFileDependencies();
            browserCache.SetLastModifiedFromFileDependencies();
        }
    }
}

we need to pass the parameters to the image for image width "w", image height "h" and file version "v". Based on those parameters dynamic image will be generated. After generating the resized image it will be cached on the client browser, server level and the application level. If we want to invalidate the cache, we can simply change the value of the parameter "v".

<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
    <httpHandlers>
      <add verb="*" path="*.png,*.jpg,*.jpeg,*.gif,*.bmp" type="CMSN.Software.Tutorials.HowToDynamicallyResizeImages.DynamicImage,HowToDynamicallyResizeImages"/>
    </httpHandlers>
  </system.web>
  <system.webServer>
    <handlers>
      <add verb="*" path="*.png,*.jpg,*.jpeg,*.gif,*.bmp" name="DynamicImage" type="CMSN.Software.Tutorials.HowToDynamicallyResizeImages.DynamicImage,HowToDynamicallyResizeImages"/>
    </handlers>
    <validation validateIntegratedModeConfiguration="false"/>
  </system.webServer>
</configuration>

In above configuration sample you can understand how to bind the asp.net Generic Web handler file to the custom file format. Here we bind the asp.net Generic Web handler to all the supporting image formats. Any request for those file format will be redirected to the asp.net Generic Web handler. For more information on binding the asp.net Generic Web handler to custom file format see How to: Configure an HTTP Handler Extension in IIS

We can use image files as usual. only different here is we need to pass the parameters to the image for image width "w", image height "h" and file version "v".
http://localhost/Images/msdn.png?w=300&h=149
http://localhost/Images/msdn.png?w=300
http://localhost/Images/msdn.png?h=149
http://localhost/Images/msdn.png

In the first method generated image size will be width = 300 and height = 149

Second method generates the image on width = 300 and height based on the image aspect ratio.

Third sample generates the image height = 149 and width based on the image aspect ratio

Next one will return the image on original size.

We can pass the parameter "v" with any of above. value of the "v" will determine the cached file version.

Download tutorial

2 comments:

Post a Comment