Using HttpHandlers to serve image files
January 28, 2009This is the first article in what will eventually be a series of articles deconstructing some of the cool functionality in BlogEngine.NET.
BlogEngine.NET makes extensive use of HttpHandlers to do everything from handling requests for images and files, to trackbacks, css, and sitemaps. I’m going to break-down the ImageHandler class and create a bare-bones version to serve images for this demonstration.
Why bother
The first thing one might ask is, why bother using HttpHandlers? In most cases a basic ASPX page can be created to serve image files, and even that may be over-kill if you can just scribble down an HTML image tag and be done with it (in instances where the files are publically accessible). To quote the very class summary in BlogEngine.Net:
/// By using a HttpHandler to serve images, it is very easy
/// to add the capability to stop bandwidth leeching or
/// to create a statistics analysis feature upon it.
While this will not be the focus of this article, that should get you thinking. Before you continue though, please read Karl Seguin’s fantastic article HttpHandlers – Learn Them. Use Them. if you haven’t already. He explains HtppHandlers in greater detail and just why you may, or may not, want to use them.
The HTTP image handler in two sentences
To give a rough overview, what we need to do is create a class that implements the IHttpHandler interface which is as simple of implementing the ProcessRequest() method and one property. The ProcessRequest() method is the heart of the HttpHandler and is where we have the code that reads the requested image file and transmits it back to the browser.
Here is the stripped-down ImageHandler, based on the one in BlogEngine.NET. (Click “+ expand source” to see the code)
public class ImageHandler : IHttpHandler
{
/// <summary>
/// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"></see> instance.
/// </summary>
/// <value></value>
/// <returns>true if the <see cref="T:System.Web.IHttpHandler"></see> 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"></see> interface.
/// </summary>
/// <param name="context">An <see cref="T:System.Web.HttpContext"></see> 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)
{
if (!string.IsNullOrEmpty(context.Request.QueryString["picture"]))
{
string fileName = context.Request.QueryString["picture"];
try
{
FileInfo fi = new FileInfo(context.Server.MapPath("~/App_Data/") + fileName);
if (fi.Exists)
{
int index = fileName.LastIndexOf(".") + 1;
string extension = fileName.Substring(index).ToUpperInvariant();
// Fix for IE not handling jpg image types
if (string.Compare(extension, "JPG") == 0)
context.Response.ContentType = "image/jpeg";
else
context.Response.ContentType = "image/" + extension;
context.Response.TransmitFile(fi.FullName);
}
else
{
context.Response.Write("Error, could not find image");
}
}
catch (Exception ex)
{
context.Response.Write("Error, an exception occured");
}
}
}
}
All that’s happening under the hood is the handler is getting a FileInfo object and then calling Response.TransmitFile(). But it’s here that you may begin to think about the payoff of using this image handler: You may wish to log this activity, raise an event, or something else. BlogEngine.NET raises a few events “OnServing” “OnServed” and “OnBadRequest”.
In my case, I put the ImageHandler.cs file directly in the website’s App_Code folder, and as a result the web.config looks like so:
<httpHandlers>
<add verb="*" path="image.axd" type="ImageHandler" validate="false"/>
</httpHandlers>
Only specifying the type is necessary. If your handler is in an external library, you’ll need to specify the assembly name as well. Refer to <add> Element for <httpHandlers>.
Now when a request is made to image.axd with a querystring “picture=somefilename”, our image handler will try to find the file in our App_Data folder and serve it back as part of the Response stream. In this case you can simply create an HTML image tag with the SRC attribute = “image.axd?picture=logo.png” and the image will be rendered in your browser.
A note about .axd and .ashx extensions
The .axd and .ashx extensions are pretty much interchangeable and because BlogEngine.NET used .axd, I have done the same. When in doubt, you may want to use .ashx. As Scott Guthrie points out, there are no built-in .ashx end-points in ASP.NET (whereas there are a few .axd ones, such as webresources.axd). Going with .ashx reduces the chance of a naming conflict.
A note about debugging HTTP Handlers
Using this example, you will find that there is actually no way to debug your handler directly through Visual Studio like you would a page. The process is much more involved, so I’d suggest you reference Dina Fleet Berry’s how-to article.
One thing you can do, is use some rudimentary Response.Write() calls to help you debug. For a simple handler, this may suffice for debugging. Note that since the HttpContext is passed to the ProcessRequest method, you simply need to call context.Response.Write().
Tags: httphandlers, image handler
Categories: BlogEngine.NET, ASP.NET, C#
