Adding an HTTP Handler to manage Images requests in .Net
This time I found myself in a very interesting challenge. We store user profile and login images in a server folder and users update their images as they need. Sometimes, when you make a release, it is just better to remove everything and paste the new version (just in case some old file remains on there) which, in case you forget, may end with the user images removed. What a problem!
As always, there are different solutions for this, like using SQL Server to store the images, but I decided to use an external folder which the application would have access and edit rights and a virtual path. Saving the images was not a deal, just using a FileStream object and Write method. The problem came when having to send the images to the user on a request.
If they are stored in a virtual path, there isn’t a real path for them in the site, there is no www.mysite.com/imgs/img.jpg, I have a \\imgs\img.jpg instead. So, how could I set an url for that image so the browser can get it?
Well, the solution was to use some virtual path and return the image when the user tried to access it, this can be done with a Module checking each request to the application, with a webservice, with a fake aspx page that returns an image instead than a page or with a handler. I decided to use the handler since it seems the best option for this purpose.
First, I made the handler just creating a new class, made it use the IHttpHandler interface to make it “interfaceable” and then added the rest of the behavior:
public class UserImagesHandler : IHttpHandler
{
public UserImagesHandler() { }
public bool IsReusable { get { return false; } }
public void ProcessRequest(HttpContext context) {
HttpRequest request = context.Request;
HttpResponse response = context.Response;
String file = System.IO.Path.GetFileName(request.Path);
context.Response.ContentType = "image/" + System.IO.Path.GetExtension(file).Replace(".", "");
if (request.RawUrl.ToLower().Contains("profile")) {
response.TransmitFile(MyNameSpace.Config.PATH_IMG_USER_PROFILE + file);
} else if (request.RawUrl.ToLower().Contains("login")) {
response.TransmitFile(MyNameSpace.Config.PATH_IMG_USER_LOGIN + file);
} else {
response.StatusCode = 404;
response.Write("Not found.");
}
}
}
The method IsReusable comes with the IHttpHandler interface and is used to determine if the handler should be kept in memory and be reused by any request or should make a new instance for every request. In our case our handler should be reusable so it handles every request saving proc time and memory.
Now, I need to say IIS to send these requests to this handler. I can do that using the IIS configuration, but that’s limited to the file extension (you can only make a handler for each file extension, you can still create a new file extension for your handler but.. that wasn’t my idea) or you can set it at the webconfig which gives you more flexibility.
There are two ways for adding this to the webconfig depending on if you are using IIS6 or IIS7, since you may have IIS6 at local and 7 at production or viceversa you may want to use both ways so it will work at any IIS.
First way, in IIS6, it is set under the <system.web> tag:
<system.web>
<httpHandlers>
<add verb="*" path="imgs/users/*/*" type="UserImagesHandler" />
</httpHandlers>
</system.web>
Second, for IIS7, under the <system.WebServer> tag:
<system.webServer>
<handlers>
<add verb="*" path="imgs/users/*/*" type="UserImagesHandler" name="UserImagesHandler" />
</handlers>
</system.webServer>
Once thats done your handler should work and the requests for the images in the virtual path will be catched and responsed with what we have at the real folder. We can also add other features like access control since this is a full request and we have access to the session, cookies or auth. cookies. We could also add querystrings to resize the responsed images, make any log or any other thing you want to control.