之前在做一个注册页面的时候想要实现一个验证码功能,于是采用了以下代码:
image.jsp//生成验证码
1 Color getRandColor(int fc,int bc) 2 { 3 Random random = new Random(); 4 if(fc>255) fc=255; 5 if(bc>255) bc=255; 6 int r=fc+random.nextInt(bc-fc); 7 int g=fc+random.nextInt(bc-fc); 8 int b=fc+random.nextInt(bc-fc); 9 return new Color(r,g,b); 10 } 11 %> 12 <% 13 out.clear();//这句针对resin服务器,如果是tomacat可以不要这句 14 response.setHeader("Pragma","No-cache"); 15 response.setHeader("Cache-Control","no-cache"); 16 response.setDateHeader("Expires", 0); 17 18 int width=60, height=20; 19 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 20 21 Graphics g = image.getGraphics(); 22 Random random = new Random(); 23 24 g.setColor(getRandColor(200,250)); 25 g.fillRect(0, 0, width, height); 26 27 g.setFont(new Font("Times New Roman",Font.PLAIN,18)); 28 29 g.setColor(getRandColor(160,200)); 30 for (int i=0;i<155;i++) 31 { 32 int x = random.nextInt(width); 33 int y = random.nextInt(height); 34 int xl = random.nextInt(12); 35 int yl = random.nextInt(12); 36 g.drawLine(x,y,x+xl,y+yl); 37 } 38 char c[] = new char[62]; 39 40 for (int i = 97, j = 0; i < 123; i++, j++) { 41 c[j] = (char) i; 42 } 43 for (int o = 65, p = 26; o < 91; o++, p++) { 44 c[p] = (char) o; 45 } 46 for (int m = 48, n = 52; m < 58; m++, n++) { 47 c[n] = (char) m; 48 } 49 String sRand=""; 50 for (int i=0;i<4;i++){ 51 int x = random.nextInt(62); 52 String rand=String.valueOf(c[x]); 53 sRand+=rand; 54 g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110))); 55 g.drawString(rand,13*i+6,16); 56 } 57 58 // 将认证码存入SESSION 59 session.setAttribute("rand",sRand); 60 61 g.dispose(); 62 try{ 63 ImageIO.write(image, "JPEG", response.getOutputStream()); 64 }catch(Exception e){ 65 e.printStackTrace(); 66 }finally{ 67 if(response.getOutputStream()!=null){ 68 try{ 69 response.getOutputStream().close(); 70 }catch(Exception e){ 71 e.printStackTrace(); 72 } 73 } 74 }
加载验证码
1 function loadimage(){ 2 document.getElementById("randImage").src = "image.jsp?"+Math.random(); 3 }
效果如图:
但是会报一个异常:
严重: Servlet.service() for servlet jsp threw exception
java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:610)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:125)
at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:118)
at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:188)
at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:118)
at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:77)
at org.apache.jsp.image_jsp._jspService(image_jsp.java:148)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:374)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:859)
at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:574)
at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1527)
at java.lang.Thread.run(Thread.java:662)
一开始我觉得是文件流没有关闭,所以给加了一个try-catch(上面代码是加了之后的),但是依然没有效果,最终询问老师。老师给加了两行代码:
out.clear();
out = pageContext.pushBody();
之后就没有异常了。
在网上搜了一下out = pageContext.pushBody();
由于jsp container在处理完成请求后会调用releasePageContet方法释放所用的PageContext object,并且同时调用getWriter方法,由于getWriter方法与在jsp页面中使用流相关的getOutputStream方法冲突,所以会造成这种异常,解决办法是:只需要在jsp页面的最后加上两条语句:
out.clear();
out=pageContext.pushBody();
即可(其中out,pageContext均为jsp内置对象!)
下面一篇随笔将会对pageContext对象做一些笔记。