Not long ago, I developed a freeware application and made it available on my site for download. In a short period of time, it has started to become somewhat popular, can tell by download stats on some of the other sites where I made it available so I wanted to be able to track the number of downloads of any of the files that were being downloaded from my site and this is the solution I came up with.
In the pipeline, the Handler is called last and when it is called, the appropriate
HTTPHandler
is executed. Since the
HTTPApplication
object will call the
HTTPHandler
to process the request and generate a response, we can use our handler to implement our call to the business layer to update the count for the file in question and fulfill the request for the download.
To accomplish this, I use a modified anchor tag instead of the usual anchor tag that makes a call to our handler like so:
<a href="path_to_file.ext" />Some Text
<a href="DownloadHandler.ashx?file=path_to_file.ext" />Some Text
The code below is a copy of the handler code that I use on my site. I have read in many places that the handler needs to be registered in the
Web.config file, but when I tried this it did not work, so I didn't register it and just call it as described above. The magic that makes this work is the few lines starting at the Content-Disposition which is the file name that is suggested in the File|Save As Dialog. Content-Length is the maximum size of the data to be transferred, caution do not set this value to low as it will end prematurely and the downloaded file will most likely be corrupt. Next, we tell it what we want to download and in this case the application/octet-stream specifies that it is a binary file and finally output the file using the
context.Response.WriteFile
.
<%@ webhandler language="C#" class="DownloadHandler"; %>
using System;
using System.Web;
using System.Web.UI.HtmlControls;
using System.IO;
public void ProcessRequest(HttpContext context)
{
string fileName = context.Request.QueryString["file"].ToString();
string filePath = HttpContext.Current.Server.MapPath("~/downloads/" + fileName);
FileInfo file = new System.IO.FileInfo(filePath);
if (!file.Exists)
{
context.Response.StatusCode = 500;
context.Response.End();
return;
}
DownloadsService.UpdateDownloadCount(fileName);
context.Response.Clear();
context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.Cache.SetNoStore();
context.Response.Cache.SetExpires(DateTime.MinValue);
context.Response.AddHeader("Content-Disposition", "attachment; filename=" + file.Name);
context.Response.AddHeader("Content-Length", file.Length.ToString());
context.Response.ContentType = "application/octet-stream";
context.Response.WriteFile(file.FullName);
context.ApplicationInstance.CompleteRequest();
context.Response.End();
}
public bool ValidateParameters(HttpContext context)
{
return true;
}
public bool IsReusable
{
get
{
return true;
}
}
}