如何实现图像访问计数器

原创 2006年06月26日 10:42:00

统计和显示页面的访问次数是一个常见的WEB应用,因为在网页每次被访问时,页面的访问次数都要发生改变,所以这个功能必须通过服务器端的程序来实现。页面访问计数器在服务器端的基本执行过程为:首先从数据库或其他存储系统中取出页面原来的访问次数,然后将原来的访问次数加1后再作为当前访问次数送给浏览器显示,同时将当前访问次数存储回数据库或其他存储系统中,以便下次访问该页面时使用。但是,一些WEB站点只能输出静态页面内容,没有开放运行服务器端程序的功能。例如,一些提供免费网页空间的站点,在客户端访问任何类型的文件时,服务器都会原封不动地输出给浏览器去处理,显然无法直接在这些只支持静态内容的WEB站点上编写服务器端程序来实现页面访问次数的统计和显示功能。于是,一些具有执行服务器端程序功能的WEB站点推出了免费的页面访问计数器,只要在位于任何站点的一个静态HTML页面中增加一条该站点提供的HTML语句,该语句就能显示出该静态页面的访问次数,从而帮助静态HTML页面实现了访问次数的统计和显示功能。一个站点要想能统计另外一个站点上的某个HTML页面的访问次数,必须让任何一个浏览器在每次访问那个HTML页面都通知这个一下站点,这可以通过在静态HTML页面中增加两种特殊的标签来实现:<img>标签和设置src属性的<script>标签。下面只分析使用<img>标签的应用情况,对于使用设置src属性的<script>标签的应用情况,则作为一道习题交给读者自己去完成。

要利用<img>标签为静态HTML页面实现访问次数的统计和显示功能,需要理解<img>标签的三个重要特性:

1)一个包含有图像的网页文件中并没有包含真正的图像数据内容,而只是使用<img>标签指明了图像的URL地址,如下所示:

       本网页已被浏览了<img src= "count.gif ">

如果浏览器在解析网页文档的过程中遇到了<img>标签,它根据<img>标签的src属性所指定的URL地址去访问WEB服务器,从WEB服务器上获取图像数据后,再在<img>标签的位置处显示出来。

2<img>标签的src属性也可以指向当前页面所在WEB服务器之外的其他WEB服务器上的图像文件。

3)浏览器并不关心<img>标签所需的图像数据在服务器端是如何产生,它只知道去访问src属性指定的URL资源,并把服务器返回的数据当作一个图像的内容来显示。服务器返回的图像数据可以直接从一个静态图像文件中读取,也可以通过Servlet程序在内存中动态创建。所以,<img>标签的src属性指向的URL不一定非得是事先静态存在的静态图像文件,也可以是一个Servlet程序。

在静态HTML页面中使用<img>标签实现免费页面访问计数器的工作原理如图5.17所示,WEB站点A上的静态页面count.html内有一条<img>标签语句,<img>标签的src属性指向了WEB站点B上的一个名为CountServletSerlvet程序,当浏览器显示从站点A上获得的count.html页面时,它将根据其中的<img>标签的src属性去访问WEB站点B上的CountServlet程序以获得要显示的图像数据,CountServlet则创建出具有该页面的访问次数的图像回送给浏览器显示。

5.17

因为WEB站点B上并不存在客户端要访问的图像文件,而是由Servlet程序在内存中临时动态生成图像内容后传送到客户端的,所以,尽管静态页面内的<img>标签所指向的这个Servlet程序的URL地址是固定不变的,但是,它每次被访问时所返回的图像内容却是可以不断变化的。

要编写一个用<img>标签统计和显示静态HTML页面的访问次数的Servlet程序,需要考虑以下一些技术细节:

l         Servlet程序输出的图像格式为jpeg,所以,它应告诉浏览器其所输出的实体内容的MIME类型为image/jpeg,浏览器才会将它所接收到的数据当作一个图像进行处理。

l         因为图像是二进制数据,所以应该调用HttpServletResponse.getOutputStream方法返回的ServletOutputStream对象来向客户端写入图像数据,而不应使用HttpServletResponse.getWriter方法返回的PrintWriter对象。

l         java.awt.image.BufferedImage类用于在内存中创建一幅图像,具体的图像内容则可以通过调用其图形上下文对象(java.awt.Graphics)的各种绘图方法生成,这里仅仅需要绘制一串数字,调用Graphics.drawString方法即可。BufferedImage类创建的内存图像的初始背景颜色和绘图颜色都是黑色的,如果想改变背景颜色,还需要先设置相应的绘图颜色,然后调用Graphics.fillRect填充整个背景。

l         在内存图像中绘制访问次数时,必须限定显示的位数,如果访问次数超过七位,则用数字9999999显示,如果访问次数不足七位,则在前面补充相应个数的0

l         每个引用该Servlet程序的静态页面的URL都对应一个各自的访问次数,每个URL及其访问次数需要使用数据库系统来进行存储,对于简单的实验,也可以采用一个属性文件来进行存储。程序首先使用Referer请求头获得当前引用页面的URL,然后去数据库或属性文件中检索该URL的访问次数,再将访问次数加1后保存进数据库或属性文件,并将访问次数绘制在内存图像中。如果属性文件中不存在该URL的信息,则说明该URL页面属于第一次统计访问。关于如何获得Referer请求头,请参看6.4节的讲解。

l         JDK中提供了一个javax.imageio.ImageIO类,它的write方法可以将BufferedImage对象中的图像编码成jpeg格式的图像数据后写入到一个OutputStream流对象中。因此,只需要调用ImageIO.write方法将BufferedImage对象中的图像编码成jpeg格式后写入到ServletOutputStream流对象中,就将整个图像数据输出给了客户端。

:动手体验:使用<img>标签实现静态HTML页面的访问次数统计和显示

1)编写一个能统计静态HTML页面的访问次数和将访问次数以图像形式返回给客户端的Servlet程序,每个页面的URL及访问次数用一个属性文件进行存储,如例程5-10所示。

例程5-10  CountServlet.java

 


import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.awt.*;

import java.awt.image.*;

import javax.imageio.ImageIO;

import java.util.Properties;

 

public class CountServlet extends HttpServlet

{

       public void doGet(HttpServletRequest request,

              HttpServletResponse response) throws ServletException,IOException

       {           

              response.setContentType("image/jpeg");

              //设置浏览器不要缓存此图片

              response.setHeader("Pragma","No-cache");

              response.setHeader("Cache-Control","no-cache");

              response.setDateHeader("Expires", 0);

 

              ServletOutputStream sos = response.getOutputStream();

 

              BufferedImage image =

                            new BufferedImage(80, 20, BufferedImage.TYPE_INT_RGB);

              Graphics g = image.getGraphics();

              /*g.setColor(Color.BLACK);

              g.fillRect(0, 0, width, height);*/

              g.setColor(Color.WHITE);

              g.setFont(new Font(null,Font.ITALIC|Font.BOLD,18));

              String value = getAccessCount(request);

              int len = value.length();

              //最大访问次数不能超过9999999

              if(len > 7)

              {

                     value = "9999999";

              }

              else

              {

                     String tmp="";

                     for(int i=0;i<7-len;i++)

                     {

                            tmp = tmp +"0";

                     }

                     value = tmp + value;

              }

              g.drawString(value,0,18);

              //结束图像的绘制过程,完成图像

              g.dispose();

      

              ImageIO.write(image, "JPEG", sos);

              sos.close();

       }

      

       private String getAccessCount(HttpServletRequest request)

       {

              //得到引用页面的URL地址,并以此作为访问次数的检索关键字

              String pageKey = request.getHeader("referer");

              if(pageKey == null)

              {

                     return "0";

              }

 

              Properties settings = new Properties();

              //count.txt等内部文件最好是保存在WEB-INF目录中

              String countFilePath = getServletContext().getRealPath("/count.txt");

              try

              {

                     //下面的语句没有使用close方法关闭流,有缺陷!

                     settings.load(new FileInputStream(countFilePath));

              }

              catch(Exception e){}

 

              String count = "0";

              try

              {

                     count = settings.getProperty(pageKey);

                     if(count == null)

                     {

                            count = "0";

                     }

                     int c = Integer.parseInt(count) + 1;

                     count = new Integer(c).toString();

                     settings.put(pageKey,count);

                     //下面的语句没有使用close方法关闭流,有缺陷!

                     settings.store(new FileOutputStream(countFilePath),

                                   "the page is accessed:");

              }

              catch(Exception e)

              {

                     System.out.println(e.getMessage());

              }

              return count;         

       }

}

 


编译CountServlet.java源文件,确保编译后生成的class文件存放在了<tomcat的安装目录>/webapps/it315/WEB-INF/classes目录中。

2)修改<tomcat的安装目录>/webapps/it315/WEB-INF/web.xml文件,在其中的相应位置处增加如下两段内容:

    <servlet>

        <servlet-name>CountServlet</servlet-name>

        <servlet-class>CountServlet</servlet-class>

    </servlet>

       ……

       ……

    <servlet-mapping>

        <servlet-name>CountServlet</servlet-name>

        <url-pattern>/servlet/CountServlet</url-pattern>

    </servlet-mapping>

保存web.xml文件后,重新启动Tomcat

3)在任何计算机上的任何HTML页面中只要加入如下语句:

       <img src="http://可对外访问的主机地址:8080/it315/servlet/CountServlet">

就可以统计和显示该页面的访问次数了。在<tomcat安装目录>/webapps/it315目录中编写一个名为count.htmlhtml文件,如例程5-11所示

例程5-11  count.html

 


<meta http-equiv="Content-Type" content="text/html;charset=GB2312">

本网页已被浏览了<img src="http://localhost:8080/it315/servlet/CountServlet">次。

 


在浏览器地址栏中输入如下地址:

       http://localhost:8080/it315/count.html

浏览器中显示的结果如图5.18所示。

5.18

刷新浏览器的访问几次,可以看到图片中的数值随之增长。

 

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

非宁静无以致远,非淡薄无以明志!

从今天起,不再发表任何与技术无关的感想,任何嬉笑怒骂,皆置于脑后.让自己从一个平庸的人变成一个真正高尚的人,而不是仅仅只是一个高尚的想法.致力于在此写下每天的备课内容和培训日记,不再考虑原创与借鉴,自...

孙卫琴感动了我!

今天听了孙卫琴的讲座,颇受感动。本来觉得孙卫琴的成功是走了一些窍门,无非是综合了国外多本书籍的成果,再用自己的语言描述出来而已,少有她自己摸索和实践的东西。即便这般认为,但孙卫琴也是令我非常尊敬和佩服...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

一个简单的怪问题

一个学员问了一个关于IO的怪问题,问题是这样的:读取键盘输入的一个字符,然后打印输出这个字符,在打印字符的前面和后面分别加了一个字符串,程序的代码如下: public class Test{     ...

一个关于Unicode字符编码的奇怪问题

有一个学员问了一个关于Unicode字符编码的奇怪问题。 问题如下:String strChina = "中国"; (1)直接把每个字符中的内容对应着的整数打印出来,显示的结果就是这个字符的Unico...

最近录制了一些视频,搭建和测试了一下视频平台

最近录制了一些视频,讲解非常详细和细致,但也随之带来一个问题,对于已经会了一些的朋友来说,就显得有点罗嗦了。对于已经会的人,总是只想看到自己正好不会和没注意的地方,可每个人不会和没注意的地方都不同,我...

普通高中生水平就能干好的编程到底是不是高科技?

我一直就想大声问问众多的IT同仁们,我们干的是高科技吗?不管你怎么说,我要回答不是,掌握struts,spring,hibernate技术应用的人,是高级人才吗?我还要说不是!虽然我知道这又要引起一些...

一个让98%的Java程序员犯难的偏门问题!

一个学员曾经问了我一个他在sun公司的论坛上看到的怪问题,因为这个问题一直没有人解答,加之这个问题所涉及的知识有点偏门,所以,我断言这个问题至少有98%的Java程序员不会!(之所以这么断言,是因为我...

要想成为优秀的IT培训教师,其实很简单!

首先,什么叫优秀的培训教师,如果大家肯定我是一个优秀的培训教师,也肯定现在网上流传的和我差不多的视频讲解者是优秀的培训教师,那我告诉你,要想成为一名和我们差不多的优秀培训教师,太容易了!不管是为了名利...

过滤器的使用实现网站访问计数器

一、创建过滤器需要使用javax.servlet.Filter接口,同时实现Filter接口的3个方法。 CountFilter: package com.home.web; import ja...

redis多种方式实现访问计数器实例详解

使用场景1 - 计数器 例如:一个web应用,我们想记录每个用户每天访问这个网站的次数。就可以使用这个用户的id和当天日期拼接一个key,每访问一次只用incr对该key操作,从而获得该用户当天的访问...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)