网页图片验证码实验心得

方法一,直接在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.

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

 

具体的原因就是

tomcat jsp 编译成 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()

原因二:

     

J2EE API 参考里有这么个 :

 

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

 

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.awt java.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");    

}

}

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

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

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(o ut );

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 中。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值