In this tutorial I’ll share how I create a c# thumbnail for each photo on our websites, and keep track of how many different sizes I need to use, while keeping images small for efficient load time, and efficient for server load. I will attach the Thumbnailer class at the end of the post. The thumbnailer can handle fixed width & height risize/crop, proportional resizing, width & height constraint resizing. The Thumbnailer can be used directly as an IHttpHandler or as a helper class for for another handler in your project.
The first thing we like to do in our projects is to create a route for all the images on the website, so we add
_routes.Add("PhotosRoute", new Route("photos/{size}/{photoUrl}", new PhotoRouteHandler()));
Notice, this way all photos are contained within a single path, and you can change the size or url of a photo, and allow the browser to cache it.
And now, to write the IRouteHandler for the above route. You’ll need to import the following libraries
using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; public class PhotoRouteHandler : IRouteHandler { public IHttpHandler GetHttpHandler(RequestContext requestContext) { requestContext.HttpContext.Response.Clear(); //Cache the photo in the browser to avoid unnecessary requests. HttpCachePolicyBase cache = requestContext.HttpContext.Response.Cache; TimeSpan cacheDuration = TimeSpan.FromSeconds(86400); cache.SetCacheability(HttpCacheability.Public); cache.SetExpires(DateTime.Now.Add(cacheDuration)); cache.SetMaxAge(cacheDuration); cache.AppendCacheExtension("must-revalidate, proxy-revalidate"); //Get the size and url of the photo string photoUrl = requestContext.RouteData.Values["photoUrl"].ToString(); string size = requestContext.RouteData.Values["size"].ToString(); //Set the content type of the response requestContext.HttpContext.Response.ContentType = GetContentType(photoUrl); Image img = null; Image source; bool resizedFile = false; //Check if the photo already exists resized on your HDD if (File.Exists(Path.Combine(@"C:MySiteContent", "Photos", size, photoUrl))) { img = Image.FromFile(Path.Combine(@"C:MySiteContent", "Photos", size, photoUrl)); resizedFile = true; } else { try { //Try to get the photo from the HDD source = Image.FromFile(Path.Combine(@"C:MySiteContentPhotos", photoUrl)); } catch { //If photo doesn't exist on your HDD, send back the default image placeholder source = Image.FromFile(Path.Combine(@"C:MySiteContentPhotos", "default.jpg")); resizedFile = true; //Used later so we don't store the placeholder } // Resize the image to completely fit into the size of the rectangle if (size == "thumb") img = Thumbnailer.FillImage(Thumbnailer.ResizeImage(source, new Size(119, 88)), new Size(119, 88), Thumbnailer.Anchor.Center); // Resize and crop the image to completely fill the rectangle else if (size == "small") img = Thumbnailer.ResizeCrop(source, new Size(284, 205)); // Resize the image to have a width of 516px else if (size == "large") img = Thumbnailer.ResizeImage(source, Thumbnailer.GetNewSize(source.Size, new Size(516, 516), Thumbnailer.ResizeMode.FixedWidth)); // Default rezise for all images where size wasn't accounted for else img = Thumbnailer.ResizeImage(source, Thumbnailer.GetNewSize(source.Size, new Size(400, 300), Thumbnailer.ResizeMode.FixedWidth)); } if (img != null) { if (GetContentType(photoUrl) == "image/png") { MemoryStream memImg = new MemoryStream(); EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 1000L); ImageCodecInfo pngCodec = Thumbnailer.getEncoderInfo("image/png"); EncoderParameters encoderParams = new EncoderParameters(1); encoderParams.Param[0] = qualityParam; if (!resizedFile) { //This is the first time this photo has been viewed at this size, so we'll save //it in a folder named after the size, so we don't have to resize it again later if (!Directory.Exists(Path.Combine(@"C:MySiteContentPhotos", size))) Directory.CreateDirectory(Path.Combine(@"C:MySiteContentPhotos", size)); img.Save(Path.Combine(@"C:MySiteContent", "Photos", size, photoUrl), pngCodec, encoderParams); } img.Save(memImg, pngCodec, encoderParams); memImg.WriteTo(requestContext.HttpContext.Response.OutputStream); img.Dispose(); } else { MemoryStream memImg = new MemoryStream(); EncoderParameter qualityParam = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L); ImageCodecInfo jpegCodec = Thumbnailer.getEncoderInfo("image/jpeg"); EncoderParameters encoderParams = new EncoderParameters(1); encoderParams.Param[0] = qualityParam; if (!resizedFile) { if (!Directory.Exists(Path.Combine(@"C:MySiteContent", "Photos", size))) Directory.CreateDirectory(Path.Combine(@"C:MySiteContentPhotos", size)); img.Save(Path.Combine(@"C:MySiteContent", "Photos", size, photoUrl), jpegCodec, encoderParams); } img.Save(memImg, jpegCodec, encoderParams); memImg.WriteTo(requestContext.HttpContext.Response.OutputStream); img.Dispose(); } } requestContext.HttpContext.Response.End(); return null; } private static string GetContentType(String path) { switch (Path.GetExtension(path)) { case ".bmp": return "image/bmp"; case ".gif": return "image/gif"; case ".jpg": return "image/jpeg"; case ".png": return "image/png"; default: break; } return ""; } }
Quick Links
Legal Stuff