网页图片验证码实验心得

来源:互联网 发布:hl中走丝系统编程 编辑:程序博客网 时间:2024/06/06 00:52

方法一,直接在JSP页面中写java代码

checkCodeImage.jsp

<%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*,java.io.OutputStream" pageEncoding="gb2312"%>
<%!
    Color getRandColor(int fc,int bc){//给定范围获得随机颜色
         Random random = new Random();
         if(fc>255) fc=255;
         if(bc>255) bc=255;
         int r=fc+random.nextInt(bc-fc);
         int g=fc+random.nextInt(bc-fc);
         int b=fc+random.nextInt(bc-fc);
         return new Color(r,g,b);
    }
%>
<%
    //设置页面不缓存
    response.setHeader("Pragma","No-cache");
    response.setHeader("Cache-Control","no-cache");
    response.setDateHeader("Expires", 0);

   
    //生成随机类
    Random random = new Random();
   
    // 在内存中创建图象
    int width=60;
    int height=20;
    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
   
    // 获取图形上下文
    Graphics g = image.getGraphics();
   
    // 设定背景色
    g.setColor(getRandColor(200,250));
    g.fillRect(0, 0, width, height);
   
    //设定字体
    g.setFont(new Font("Times New Roman",Font.PLAIN,18));
   
    //画边框
    //g.setColor(new Color());
    //g.drawRect(0,0,width-1,height-1);
   
    // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
    g.setColor(getRandColor(160,200));
    for (int i=0;i<155;i++)
    {
         int x = random.nextInt(width);
         int y = random.nextInt(height);
         int xl = random.nextInt(12);
         int yl = random.nextInt(12);
         g.drawLine(x,y,x+xl,y+yl);
    }
   
    // 取随机产生的认证码(4位数字)
    String sRand="";
    for (int i=0;i<4;i++){
        String rand=String.valueOf(random.nextInt(10));
        sRand+=rand;
        // 将认证码显示到图象中
        g.setColor(new Color(20+random.nextInt(110),
                             20+random.nextInt(110),
                             20+random.nextInt(110)));//调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
        g.drawString(rand,13*i+6,16);
    }
   
    // 将认证码存入SESSION
    session.setAttribute("checkCode",sRand);

   
    // 图象生效
    g.dispose();
   
    // 输出图象到页面
    OutputStream os=response.getOutputStream();
    ImageIO.write(image, "JPEG", os);
    os.flush();
    os.close();
    os=null;
    response.flushBuffer();
    out.clear();
    out = pageContext.pushBody();

%>

使用:

<img src="checkCodeImage.jsp" width="60" height="20">

注意:

如果没有红色那段代码会出现

getOutputStream() has already been called for this response异常

解决方法:

http://blog.csdn.net/iron_wang/archive/2009/05/20/4204672.aspx

1.

tomcat5jsp出现getOutputStream() has already been called for this response异常的原因和解决方法在tomcat5jsp中出现此错误一般都是在jsp中使用了输出流(如输出图片验证码,文件下载等),没有妥善处理好的原因。

 

具体的原因就是

tomcatjsp编译成servlet之后在函数_jspService(HttpServletRequest request, HttpServletResponse response)的最后

有一段这样的代码

finally {

      if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);

    }

这里是在释放在jsp中使用的对象,会调用response.getWriter(),因为这个方法是和

response.getOutputStream()相冲突的!所以会出现以上这个异常。

 

然后当然是要提出解决的办法,其实挺简单的(并不是和某些朋友说的那样--

jsp内的所有空格和回车符号所有都删除掉),

 

在使用完输出流以后调用以下两行代码即可:

out.clear();

out = pageContext.pushBody();

2

getOutputStream() has already been called for this response问题的解决

jsp向页面输出图片的时候,使用response.getOutputStream()会有这样的提示:java.lang.IllegalStateException:getOutputStream() has already been called for this response,会抛出Exception

 

原因一:

JSP默认的输出流为PrintWriter ,<% %>以外的东西所默认的输出方式,如果你尝试在JSP中使用ServletOutputStream就会引起错误.要嘛直接改用Servlet输出(复写service方法),要嘛删除除%><%中的任何东西

(包括HTML标签,空格,回车等东西)应该就可以。

对于这样的情况应该这样来解决,删除%><%之间的所有内容包括空格和换行符,最后也要消除空格和换行符,

最好再加上一句response.reset()

原因二:

     

J2EEAPI参考里有这么个:

 

ServletResponsegetWriter()方法里会抛出这个异常,

 

IllegalStateException - if the getOutputStream method has already been called 

for this response object

 

而它的getOutputStream()方法里会抛出这个异常.

 

IllegalStateException - if the getOutputStream method has already been called for this response object

 

并且两者的函数申明里都有这么样的一句

Either this method or getOutputStream() may be called to write the body, not both.

Either this method or getWriter() may be called to write the body, not both.

 

 

以上说明也解释了为什么在往页面中写入图片的时候要使用如下循环格式

OutputStream output=response.getOutputStream();

while((len=in.read(b)) >0) 

{

output.write(b,0,len); 

 

}

output.flush();

而不是把response.getOutputStream().write()放到循环体内

 

在页面中直接写:

<body bgcolor="#ffffff">

<h1>

<%

response.getOutputStream();

%>

</h1>

</body>

将会出现错误消息如下:

java.lang.IllegalStateException: getOutputStream() has already been called for this response

org.apache.catalina.connector.Response.getWriter(Response.java:604)

org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)

org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:125)

org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:118)

 

 

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/iron_wang/archive/2009/05/20/4204672.aspx

 

方法二:在servlet中处理

我的Servlet代码如下:

注意com.sun.image.codec.jpeg包位于jdk目录的rt.jar包中,它不是公开的api,需要将rt.jar复制到web应用程序的web-inf/lib下。

import java.io.IOException;
       import java.io.OutputStream;
       import java.util.Random;

       import java.awt.*;
       import java.awt.image.*;


       import com.sun.image.codec.jpeg.JPEGEncodeParam;
       import com.sun.image.codec.jpeg.JPEGImageEncoder;
       import com.sun.image.codec.jpeg.JPEGCodec;


import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class ImageProduceServlet extends HttpServlet {


public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        //设置ContentType
        response.setContentType("text/jpeg");
        //设置页面不缓存
        response.setHeader("Pragma","No-cache");
        response.setHeader("Cache-Control","no-cache");
        response.setDateHeader("Expires", 0);
        //生成并输出图片
        String s=this.createImg(response.getOutputStream());
        //session记录生成的随机验证码
        HttpSession mySession=request.getSession();
        mySession.setAttribute("checkCode", s);

       
    }

private String createImg(OutputStream os){
        //生成随机类
        Random random = new Random();
       
        // 在内存中创建图象
        int width=60;
        int height=20;
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
       
        // 获取图形上下文
        Graphics g = image.getGraphics();
       
        // 设定背景色
        g.setColor(getRandColor(200,250));
        g.fillRect(0, 0, width, height);
       
        //设定字体
        g.setFont(new Font("Times New Roman",Font.PLAIN,18));
       
        //画边框
        //g.setColor(new Color());
        //g.drawRect(0,0,width-1,height-1);
       
        // 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
        g.setColor(getRandColor(160,200));
        for (int i=0;i<155;i++)
        {
             int x = random.nextInt(width);
             int y = random.nextInt(height);
             int xl = random.nextInt(12);
             int yl = random.nextInt(12);
             g.drawLine(x,y,x+xl,y+yl);
        }
       
        // 取随机产生的认证码(4位数字)
        String sRand="";
        for (int i=0;i<4;i++){
            String rand=String.valueOf(random.nextInt(10));
            sRand+=rand;
            // 将认证码显示到图象中
            g.setColor(new Color(20+random.nextInt(110),
                                 20+random.nextInt(110),
                                 20+random.nextInt(110)));//调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
            g.drawString(rand,13*i+6,16);
        }
       
       
        // 图象生效
        g.dispose();
       
        // encode:    
        JPEGImageEncoder  encoder =JPEGCodec.createJPEGEncoder(os);
        JPEGEncodeParam   param =  JPEGCodec. getDefaultJPEGEncodeParam(image);
        param.setQuality(1.0f, false);      
        encoder.setJPEGEncodeParam(param);    
        try {           
            encoder.encode(image);     
         } catch(IOException ioe) {      
                ioe.printStackTrace();    
         }  
         return sRand;

    }
    /**
     * 给定范围获得随机颜色
     * @param fc
     * @param bc
     * @return
     */
    private Color getRandColor(int fc,int bc){
         Random random = new Random();
         if(fc>255) fc=255;
         if(bc>255) bc=255;
         int r=fc+random.nextInt(bc-fc);
         int g=fc+random.nextInt(bc-fc);
         int b=fc+random.nextInt(bc-fc);
         return new Color(r,g,b);
    }

}

使用:

<img src="servlet/ImageProduceServlet" width="60" height="20">

解释:

http://www.computersci.net/articles/jdk1.2/docs/guide/2d/api-jpeg/com/sun/image/codec/jpeg/JPEGCodec.html

以上是类

JPEGCodec 的API

本质上,浏览器向服务器请求静态图片如jpeg时,服务器返回的仍然是标准的http响应,只不过http头的contenttype不是text/html而是image/jpeg而已,因此,我们在servlet只要设置好contenttype然后发送图像的数据流,浏览器就能正确解析并显示出图片。java中,java.awtjava.awt.image包提供了基本的绘制图像的能力,我们可以在内存中绘制好需要的图形,然后编码成jpeg或其他图像格式,最后发送相应给浏览器即可。

下面是使用servlet动态创建图像的详细步骤:

1创建bufferedimage对象,该对象存在内存中,负责保存绘制的图像;

2创建graphics2d对象,该对象负责绘制所需的图像;

3当绘制完成后,调用com.sun.image.codec.jpeg包的jpeg编码器对其编码

4最后将编码后的数据输出至httpresponse即可。

注意com.sun.image.codec.jpeg包位于jdk目录的rt.jar包中,它不是公开的api,需要将rt.jar复制到web应用程序的web-inf/lib下。

1我们先创建一个最简单的servlet

public class createimageservlet extends httpservlet {    

protected void doget(httpservletrequest request, httpservletresponse response)               throws servletexception, ioexception    {  

response.setcontenttype("image/jpeg");    

}

}

我们首先设置了responsecontenttypeimage/jpeg,这样浏览器就可以正确识别。

2然后,创建一个大小为100x100bufferedimage对象,准备绘图:

int width = 100;

int height = 100;

bufferedimage bi = new bufferedimage(width, height, bufferedimage.type_int_rgb);

3接着,bufferedimage对象中获取graphics2d对象并绘图:

graphics2d g = bi.creategraphics(); // 创建graphics2d对象

g.setbackground(color.blue);// 填充背景为白色    

g.clearrect(0, 0, width, height);

g.setcolor(color.red);// 设置前景色

// 开始绘图:

g.drawline(0, 0, 99, 99); // 绘制一条直线

g.dispose();

bi.flush();// 绘图完成,释放资源

4然后,对bufferedimage进行jpeg编码:

JPEGImageEncoder  encoder =JPEGCodec.createJPEGEncoder(out);

JPEGEncodeParam   param =  JPEGCodec. getDefaultJPEGEncodeParam(bi); 

param.setQuality(1.0f, false);       

encoder.setJPEGEncodeParam(param);     

try {            

encoder.encode(bi);      

} catch(IOException ioe) {       

ioe.printStackTrace();     

}  

5.编码后的jpeg图像直接输出到了out对象中,

  我们只要传入response. getoutputstream()就可以直接输出到httpresponse中。