Click here to Skip to main content
Click here to Skip to main content

Page Hit Counter using ASP.NET and JScript .NET

, 4 Jul 2002
Rate this:
Please Sign up or sign in to vote.
An ASP.NET counter that's easy to use from plain HTML

Introduction

It is sometimes useful to display the number of visits a page on a Web site has experienced using a counter that's displayed as part of the page's content. While adding a counter to a page written in ASP or ASP.NET is relatively straight-forward, adding a counter to a plain HTML page (no scripting of any kind) is a little different.

This article describes a counter I created (see the following figure) using ASP.NET. The ASP.NET code generates an image using the Graphics and Bitmap classes, making it easy to use on plain HTML pages without client side scripting. The code also maintains its own little "database" so that you don't have to FTP to your Web server to create a new counter. I use and designed this counter for my eBay listings and various other pages; as a result, it's designed for speed so it does not use a database or XML (but it can easily be upgraded to do that).

Sample Counter

Overview of JScript .NET

JScript .NET is one of the programming languages available on the .NET Framework and is Microsoft's version of ECMAScript - the language that's forms the basis of JavaScript. JScript .NET is very similar to C# and C++ in many ways, so if you know either language you'll immediately feel at home with JScript .NET.

JScript .NET is an object oriented language that supports classes, inheritance, polymorphism, and all other popular OO features. You can also use JScript .NET to create Windows Forms-based applications (applications that have a GUI), Windows Services, console applications, Web Services, and ASP.NET pages and applications.

Using the Counter

The following listing demonstrates how easily it is to use the counter with plain HTML:

<img src="EWCounter.aspx?item=CodeProjectArticle">

The HTML img tag's src attribute refers to an ASP.NET page that handles the details of retrieving the counter's current value from a file, updating the counter, and returning an image that contains, among other things, the value of the counter. You can add a link to the image, making the counter click-able, as shown in the following listing:

<a href="http://www.designs2solutions.com" target="_blank">
  <img src="EWCounter.aspx?item=CodeProjectArticle" border="0">
</a>

Managing the Counter's Data

The counter keeps track of the value of a counter using simple text files that it accesses using the File, FileStream, StreamReader, and StreamWriter classes. Each file contains the value of a single counter creating a one-to-one relationship between a counter and its data file, making counter reads and updates fast. The following listing shows how the counter creates a new file for a counter that does not exist:

var itm :String = Request.QueryString("item");
// ... elsewhere on the page ...
createCounter(itm);
// etc...

function createCounter(nameOfItem:String)
{
  var sw : StreamWriter;
  var filePath:String = Server.MapPath("data\\") + nameOfItem + ".txt";
  if(!File.Exists(filePath)) {
    sw=File.CreateText(filePath);
    sw.WriteLine("99");
    sw.Close();
  }
}

The initial value of the counter is 99, to make it look like the first visitor to a page is the hundredth visitor (gotta be optimistic). To create a new counter, specify a new, unique value for the item on the QueryString as shown in the preceding HTML code.

The code that retrieves and optionally updates the counter's value is straightforward. The code does not perform any random access in the file and simply reads the value using a StreamReader and optionally writes the updated value using a StreamWriter, as shown in the following listing:

function getCounterValue(nameOfItem : String, readOnly : Boolean) : String
{
  var fileIn:StreamReader = 
    File.OpenText(Server.MapPath("data\\" + nameOfItem + ".txt"),
    FileMode.Open);
  var current : String = new String("");
  current = fileIn.ReadLine().ToString();
  fileIn.Close();
  if(readOnly)
    return current;
    
  var curInt : int = Convert.ToInt32(current);
  curInt++;
  var fs : FileStream  = 
    new FileStream(Server.MapPath("data\\" + nameOfItem + ".txt"), 
    FileMode.Open,FileAccess.Write);
  var sw : StreamWriter;
  sw  = new StreamWriter(fs);
  sw.WriteLine(Convert.ToString(curInt));
  sw.Close();
  fs.Close();
  return Convert.ToString(curInt);
}

The code reads the current value of the counter using the StreamReader.ReadLine method and optionally updates it using the StreamReader.WriteLine method - simple and quick.

Rendering the Counter

The counter wouldn't be all that interesting if it's displayed as a simple text string, so the code uses the Bitmap and Graphics classes to create an in-memory representation of an image that's streamed directly to the client; as a result, the counter appears to be an image even though it's just s stream of bits coming that are sent to the client through the Response object.

The first thing the code does is create a Bitmap object that's streamed to the client and manipulated using the Graphics object, as shown in the following listing:

function drawCounter(nameOfItem : String, readOnly : Boolean)
{

  // some code omitted for brevity...  
  var height : int = 20;
  var width : int = 100;

  var bmp : Bitmap= new Bitmap(width, height);
  var g:Graphics = Graphics.FromImage(bmp);

  // Create a DarkBlue background
  g.FillRectangle(new SolidBrush(Color.DarkBlue), 0, 0, width, height);
  // LightGreen  for the small box on the left...
  g.FillRectangle(new SolidBrush(Color.LightGreen), 0, 0, 26, 20);

The preceding listing establishes the two objects it needs to work with and creates the dark blue box that the counter's value appears in, and the light green box on the left side of the counter that hosts the counter's logo.

The counter's logo is made up of three characters: "D2S". You can add text to a bitmap using the Graphics.DrawString method. Before you do that, however, you need to establish what color to use to draw the text and the text's font and the last thing you need to do is position the text within the box, as shown in the following listing:

  var maroonBrush:SolidBrush= new SolidBrush(Color.Maroon);
  var blackBrush:SolidBrush= new SolidBrush(Color.Black);
  
  var counterByFont:Font = new Font("Verdana", 7, FontStyle.Bold);
  g.DrawString("D", counterByFont,blackBrush ,0,5);
  g.DrawString("2", counterByFont,maroonBrush ,8,2);
  g.DrawString("S", counterByFont,blackBrush ,16,5);

The logo's text was easy to add to the bitmap because its content does not change. The counter's value does change, so you need to pay a little more attention to how you position it within the enclosing rectangle. As in the preceding listing, the code uses the Font and SolidBursh objects as shown:

  var counterFont:Font = new Font("Verdana", 8, FontStyle.Bold);
  var whiteBrush:SolidBrush= new SolidBrush(Color.White);

When you position something on a bitmap, you place it by defining its location in terms of the top, left pixel of the object you want to draw. Since the counter's length can vary (as the value of the counter increases), the code measures the length of the counter's value as it appears in the enclosing box and positions it so that all of the numbers are visible, as shown in the following listing:

  var counterText : SizeF;
  counterText = g.MeasureString(counterValue,counterFont);
  g.DrawString(counterValue, counterFont, whiteBrush ,
    (bmp.Width)-((counterText.Width)+5),3);

There's a lot going on in that short listing: it begins by measuring the counter's value based on the font it will be rendered in using the Graphics.MeasureString method and SizeF object. The code then renders the string using the Graphics.DrawString and positions the text to appear 3 pixels from the edge of the enclosing box.

With the counter ready to be presented, the last thing the code does is deliver it to the client in the form of a JPG image using the following line:

  bmp.Save(Response.OutputStream, ImageFormat.Jpeg);

Limiting Counter Updates

The code you have seen so far isn't very selective when it comes to updating the counter: when a user refreshes a page, the counter gets updated. While this is okay in most cases, in some cases you want to ensure that you don't have users sitting around refreshing the page every few seconds to 'dope' the value of the counter (cause it to misstate the number of visitors by artificially inflating the value).

One solution to this problem is to use a cookie that expires after a set period of time. If the cookie hasn't expired, the counter's value does not get updated but it still gets displayed. Working with cookies in ASP.NET is relatively straight-forward as long as you're aware of the fact that an expired cookie is equivalent to a null / nonexistent cookie. The following listing shows the code that manages the cookie and sets the value of the readOnly which determines if the counter is updated or just displayed:

var cookie:HttpCookie;
cookie = Request.Cookies("LastVisit");

var readOnly:Boolean = (cookie!=null);

drawCounter(itm,readOnly);

if(!readOnly) {
  cookie = new HttpCookie("LastVisit",DateTime.Now.AddSeconds(120));
  cookie.Expires = DateTime.Now.AddSeconds(60);
  Response.AppendCookie(cookie);
}

The code queries for a cookie called "LastVisit" - if the cookie does not exist or has expired it is null. After the counter is displayed and updated (depending on the value of readOnly), the code again evaluates readOnly to generate a new cookie, in case this is the user's first visit or if the cookie has expired. In this case, the code sets the cookie to expire in two minutes. Note that this solution may not work in all cases, depending on the user's browser security settings (users can configure their browsers to reject cookies from third party sites); however, this solution probably stops the majority of users from 'doping' the value of the counter.

Summary

This article described how to create a fast page hit counter that's easy to use with plain HTML. The code uses ASP.NET and the JScript .NET programming language and includes features like a logo, automatically creating new counters, and preventing users from artificially inflating the value of the counter.

Installing and Using the Sample Code

Use the following steps to install the sample:

  1. Download the code.
  2. Extract the code to a folder on your system.
  3. Use the IIS Admin Tool or the folder's properties to create a new virtual directory.
  4. Create a folder called data under the new virtual directory.
  5. Grant read, modify, and write permission to the IIS User account.
  6. Point your browser to the new virtual directory.

You should see a page that contains a short message and the counter image.

<!----------------------------- Article Ends ----------------------------->

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Erik Westermann
Software Developer (Senior)
Canada Canada
Erik is a senior developer-writer with more than 14 years professional programming experience designing and developing large scale database and Internet-centric applications for organizations including MSN.ca, ADP, Nortel, lemontonic.com, EDS, Merrill Lynch, ePost, CIBC, TD Securities, IBC, CIHI, InnovaPost, etc.
 
Erik has been specializing in BizTalk Server-based solutions for five contiguous years to date. His experience includes many SOA and ESB-style applications using technologies like Commerce Server, SharePoint, ASP.NET and advanced .NET Framework.
 

Erik's interests include systems architecture, writing, reading and his kids. Erik's affiliations include ACM (acm.org), and the IASA.

Comments and Discussions

 
GeneralKind of out of date PinmentorErik Westermann6-Aug-09 10:59 
GeneralPage Hit Counter using ASP.NET and JScript .NET Pinmemberamleaf6-Aug-09 10:38 
GeneralMy vote of 1 Pinmemberrahultinku5-Jul-09 5:34 
GeneralNice PinmemberRadu Gruian19-Dec-07 4:29 
GeneralI get the following error! Please help! Pinmembertomlovenancy200512-Nov-07 5:28 
GeneralReg: counters Pinmembersakthiraj26-Nov-05 3:23 
Generalred cross, not working PinmemberDrieske19-Apr-04 21:13 
GeneralRe: red cross, not working PinmemberErik Westermann20-Apr-04 8:26 
GeneralGraphics too compressed PinmemberTemplMetaProg24-May-03 4:14 
GeneralRe: Graphics too compressed PinmemberGLMnet4-Feb-06 14:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 5 Jul 2002
Article Copyright 2002 by Erik Westermann
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid