Dynamically generating thumbnail images in ASP.NET with C# without affecting initial page load time

SheoNarayan
Posted by in ASP.NET category on for Intermediate level | Views : 41959 red flag
Rating: 5 out of 5  
 2 vote(s)

Creating thumbnail with ASP.NET with C#. There are several ways to do that but this way I feel much better and efficient and also this doesn't affect initial page load time.
Introduction

While working for a website or an web application, you must have came across a situation where you need to display a thumbnail images for a larger images. There are several work around for it but I am going to show how to do that in the much better and efficient way, in this way your initial page load time will not be delayed because of the thumbnail images.

Approach

My approach of creating thumbnail is to create an arrays of bytes at run time and specify the img src attribute to it. I will also display NoImage.gif if my no source is being given to the function or any error occurred while dynamically generating thumbnail images.

To do this you will have to create a simple .aspx page that contains nothing but a Page_Load event.
ShowImage.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ShowImage.aspx.cs" Inherits="images_ShowImage" %>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Show Image</title>
</head>
<body>
<form id="form1" runat="server">
<div>

</div>
</form>
</body>
</html>

Just right click to your project and add a .aspx page with default contents mostly similar to above.

ShoeImage.ascx.cs
protected void Page_Load(object sender, EventArgs e)

{
if (!Page.IsPostBack)
{
Response.Clear();
int maxHeight = 200 // you can get from your config file;
int maxWidth = 250 // you can get from your config file;
if (Request["width"] != null)
{
maxWidth = Int32.Parse(Request["width"].ToString());
}
if (Request["height"] != null)
{
maxHeight = Int32.Parse(Request["height"].ToString());
}


string imageName = Request.QueryString["image"];
string extension = System.IO.Path.GetExtension(Server.MapPath(imageName));

byte[] pBuffer = Utility.CreateThumbnail(imageName, maxHeight, maxWidth, extension);
//set response content type: image/jpeg, image/gif, image/png, etc...
Response.ContentType = "image/" + extension;
//write the image to the output stream
Response.OutputStream.Write(pBuffer, 0, pBuffer.Length);

Response.End();
}
}

In the above Page_Load method, first I am clearing all the contents of the page by using Response.Clear() then I am getting my standard height and width of my thumbnail images. As specified you may specify it in your config file and get it here.

Again, I am checking if I am getting any width and height attributes as a querystring then I am storing them into my width and height variable. I am also getting image path from the "image" querystring, as I am trying to get thumbnail created for variety of images format so I am getting the extension of the image name too here itself.

Now I have declared a byte variable and calling my function that will actually generate the thumbnail image. You can keep the following function in any of your generic .cs file, In my case I have kept into utility.cs file.

CreateThumbnail function
/// <summary>

/// Create thumbnail of the image
/// </summary>
/// <param name="path"></param>
/// <param name="maxHeight"></param>
/// <param name="maxWidth"></param>
/// <param name="extension"></param>
/// <returns></returns>
public static byte[] CreateThumbnail(string path, int maxHeight, int maxWidth, string extension)
{
path = HttpContext.Current.Server.MapPath(path);
System.Drawing.Image img = System.Drawing.Image.FromFile(path);
extension = extension.ToLower();

byte[] buffer = null;
try
{
int width = img.Size.Width;
int height = img.Size.Height;

bool doWidthResize = (maxWidth > 0 && width > maxWidth && width > maxHeight);
bool doHeightResize = (maxHeight > 0 && height > maxHeight && height > maxWidth);

//only resize if the image is bigger than the max
if (doWidthResize || doHeightResize)
{
int iStart;
Decimal divider;
if (doWidthResize)
{
iStart = width;
divider = Math.Abs((Decimal)iStart / (Decimal)maxWidth);
width = maxWidth;
height = (int)Math.Round((Decimal)(height / divider));
}
else
{
iStart = height;
divider = Math.Abs((Decimal)iStart / (Decimal)maxHeight);
height = maxHeight;
width = (int)Math.Round((Decimal)(width / divider));
}
System.Drawing.Image newImg = img.GetThumbnailImage(width, height, null, new System.IntPtr());
try
{
using (MemoryStream ms = new MemoryStream())
{
if (extension.IndexOf("jpg") > -1)
{
newImg.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
}
else if (extension.IndexOf("png") > -1)
{
newImg.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
}
else if (extension.IndexOf("gif") > -1)
{
newImg.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
}
else // if (extension.IndexOf("bmp") > -1)
{
newImg.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
}
buffer = ms.ToArray();
}
}
finally
{
newImg.Dispose();
}
}
}
catch
{
System.Drawing.Image imNoimage = System.Drawing.Image.FromFile("/images/noimage.gif");
try
{
using (MemoryStream ms = new MemoryStream())
{
imNoimage.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
buffer = ms.ToArray();
}
}
finally
{
imNoimage.Dispose();
}
}
finally
{
img.Dispose();
}
return buffer;
}

In the above function, i am getting the actual path of the image and creating an image object from the image path parameter passed to this function. Now I am getting the width and height property of the image I got and deciding whether I need to resize my image or not to best fit based on the standard height and width I am getting from my Page_Laod method.

After I have decided the best fit height and width of the image I am creating an another image object newImg and getting thumbnail (img.GetThumbnailImage(width, height, null, new System.IntPtr());) using the GetThumbnailImage method of the above image object.

Now I have my thumbnail created as newImg, I have to save it to the MemoryStream based on the image format I have got from the Page_Laod (.gif, .jpg etc). For that I am checking the extension of the image I have got and saving the image in that format using newImg.Save(ms, System.Drawing.Imaging.ms, System.Drawing.Imaging.ImageFormat.Jpeg/png/gif););. Once I have image saved into MemoryStream, I am converting it into array of bytes by using ToArray() method of MemoryStream (ms.ToArray()).

Now what if an error occurred, in that case I need to display my NoImage,gif , for that I have written my code into Catch block in the same way I had written for my image I had got from Page_Load method (creating Image object from the noimage.gif and converting that into bytes of array).

At last this function is returning array of bytes to my Page_Load method.

Now, In Page_Load method of the ShowImage.aspx I have my thumbnail image in the form of bytes of array. The next thing I have to do is to specify the src attributes of img to this byte. For this I have written following code

// Exerts from above function
Response.ContentType = "image/" + extension;
Response.OutputStream.Write(pBuffer, 0, pBuffer.Length);
Response.End();

I am setting the response content type based on the image extension I got as source by querystring, then I am specifying OutputStream.Write method of Respose object with an array of bytes and its length as parameter ( Response.OutputStream.Write(pBuffer, 0, pBuffer.Length);). As soon as I did this I am done so I am instructing to end the response now for this page by specifying Response.End() method.

How to call

To call the above created thumbnail functionality you need to specify the src attribute of the img element to ShowImage.aspx file and specify the requeired querystring value (In my case image is must and width and height is optional)
<img src="/images/ShowImage.aspx?image=/images/sheonarayan.gif" />[


OR

<img src="/images/ShowImage.aspx?image=/images/sheonarayan.gif&width=200&height=250" />



Conclusion

When you will use above way of creating thumbnail images, you will notice that your entire pages will be loaded then one by one your thumbnail images will be generated on the fly and it will be displayed on the page so It is not affecting your initial page load time.

If you have any comment of suggestions, please write to me.

Thanks and Happy coding !!!
Page copy protected against web site content infringement by Copyscape

About the Author

SheoNarayan
Full Name: Sheo Narayan
Member Level: HonoraryPlatinum
Member Status: Administrator
Member Since: 7/8/2008 6:32:14 PM
Country: India
Regards, Sheo Narayan http://www.dotnetfunda.com

Ex-Microsoft MVP, Author, Writer, Mentor & architecting applications since year 2001. Connect me on http://www.facebook.com/sheo.narayan | https://twitter.com/sheonarayan | http://www.linkedin.com/in/sheonarayan

Login to vote for this post.

Comments or Responses

Posted by: Sruthi on: 8/5/2008
hai narayan,
its show an error taht path cannot found,i am not getting at all,i giving right path,can u help me?
Posted by: SheoNarayan on: 8/5/2008
Just try specifying path from the root of your application.

<img src="/images/ShowImage.aspx?image=/images/sheonarayan.gif" />

Here, in this case both files ShowImage.aspx and sheonarayan.gif both are kept in images folder.

Thanks
Posted by: Strychtur on: 7/20/2010
Hi Sheo,

Hope you are still watching this.
This is not working for me.
Brief overview: I have a repeater, inside that is a gridview. In the gridview are a bunch of hyperlinks.
What I want to achieve is when a user hovers over a hyperlink I want a thumbnail of the image to appear. when a user moves off the hyperlink get rid of the thumbnail. I used a hover menu extender to achieve this effect.
Here is the item template:
<ItemTemplate>

<asp:Panel ID="pnlPopupThumb" runat="server" CssClass="popupMenu">
<asp:Image ID="imgThumb" runat="server" ImageUrl='<%# "../dental/ShowImage.aspx?image=../documents/" + Eval("folder") +"/" + Eval("filename") %>' />
<asp:Image ID="Image1" runat="server" ImageUrl='<%# "../documents/" + Eval("folder") +"/" + Eval("filename") %>' />
</asp:Panel>
<cc1:HoverMenuExtender ID="hmePopupThumb" runat="server" TargetControlID="hyprGridDocLink" PopupControlID="pnlPopupThumb" PopupPosition="Right" OffsetX="6" PopDelay="10" HoverCssClass="popupHover"> </cc1:HoverMenuExtender>
<asp:HyperLink ID="hyprGridDocLink" runat="server" Text='<%# Eval("filename") %>' NavigateUrl='<%# "~/documents/" + Eval("folder") +"/" + Eval("filename") %>' Target="_blank" />
</ItemTemplate
>

Image1 is just a test image to make sure the path is good it works fine.
imgThumb is the one wired to Showthumb.aspx. I keep getting a broken image there. Can you help please?

Thanks
strychtur
Posted by: Strychtur on: 7/20/2010
One more thing I forgot to mention.
The buffer from utility.cs is being returned as null.
Thanks
Strychtur
Posted by: Diwali on: 5/20/2011 | Points: 25
Hi
Im new to dot net
I have used the code for thumbnail but it does not work for some of the images; get the following error. Can you help me

Server Error in '/' Application.
Object reference not set to an instance of an object.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.

Source Error:

Line 58: //write the image to the output stream
Line 59:
Line 60: Response.OutputStream.Write(pBuffer, 0, pBuffer.Length);
Line 61:
Line 62:


Source File: d:\inetpub\vhosts\mydomain.com\httpdocs\utility\ShowThumbImage.aspx Line: 60

Stack Trace:

[NullReferenceException: Object reference not set to an instance of an object.]
ASP.utility_showthumbimage_aspx.Page_Load(Object sender, EventArgs e) in d:\inetpub\vhosts\mydomain.com\httpdocs\utility\ShowThumbImage.aspx:60
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35
System.Web.UI.Control.OnLoad(EventArgs e) +91
System.Web.UI.Control.LoadRecursive() +74
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2207

Posted by: Bkuhn on: 5/31/2012 | Points: 25

This works great for me. Fits my requirements perfectly!

Many thanks!!!


Posted by: Pcinja on: 2/20/2013 | Points: 25
Hi Sheo,
Here is working code of your article: https://www.dropbox.com/s/rgoo26eyrh3v5o4/CreatingThumbnail.rar
Very good. Thank you very much
Goran
Posted by: Brunoaduarte on: 4/4/2013 | Points: 25
Awesome ! Just one question, is it possible to cache the generated thumbnails ? My images are being showed inside an updatepanel, and everytime i click one of them to open the original image link, a postback is generated and all the thumbnails have to be re-generated.

Login to post response

Comment using Facebook(Author doesn't get notification)