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