通过Servlet生成验证码图片(转)

9/6/2015来源:Java教程人气:7021

通过Servlet生成验证码图片(转)原文地址:http://www.cnblogs.com/xdp-gacl/p/3798190.html一、BufferedImage类介绍

生成验证码图片主要用到了一个BufferedImage类,如下:

创建一个DrawImage Servlet,用来生成验证码图片

复制代码
  1 package gacl.response.study;  2 import java.awt.Color;  3 import java.awt.Font;  4 import java.awt.Graphics;  5 import java.awt.Graphics2D;  6 import java.awt.image.BufferedImage;  7 import java.io.IOException;  8 import java.util.Random;  9 import javax.imageio.ImageIO; 10 import javax.servlet.ServletException; 11 import javax.servlet.http.HttpServlet; 12 import javax.servlet.http.HttpServletRequest; 13 import javax.servlet.http.HttpServletResponse; 14 /** 15  * 生成随机图片,用来作为验证码 16  */ 17 public class DrawImage extends HttpServlet { 18     PRivate static final long serialVersionUID = 3038623696184546092L; 19      20     public static final int WIDTH = 120;//生成的图片的宽度 21     public static final int HEIGHT = 30;//生成的图片的高度 22  23     public void doGet(HttpServletRequest request, HttpServletResponse response) 24             throws ServletException, IOException { 25         this.doPost(request, response); 26     } 27  28     public void doPost(HttpServletRequest request, HttpServletResponse response) 29             throws ServletException, IOException { 30         String createTypeFlag = request.getParameter("createTypeFlag");//接收客户端传递的createTypeFlag标识 31         //1.在内存中创建一张图片 32         BufferedImage bi = new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB); 33         //2.得到图片 34         Graphics g = bi.getGraphics(); 35         //3.设置图片的背影色 36         setBackGround(g); 37         //4.设置图片的边框 38         setBorder(g); 39         //5.在图片上画干扰线 40         drawRandomLine(g); 41         //6.写在图片上随机数 42         //String random = drawRandomNum((Graphics2D) g,"ch");//生成中文验证码图片 43         //String random = drawRandomNum((Graphics2D) g,"nl");//生成数字和字母组合的验证码图片 44         //String random = drawRandomNum((Graphics2D) g,"n");//生成纯数字的验证码图片 45         //String random = drawRandomNum((Graphics2D) g,"l");//生成纯字母的验证码图片 46         String random = drawRandomNum((Graphics2D) g,createTypeFlag);//根据客户端传递的createTypeFlag标识生成验证码图片 47         //7.将随机数存在session中 48         request.getSession().setAttribute("checkcode", random); 49         //8.设置响应头通知浏览器以图片的形式打开 50         response.setContentType("image/jpeg");//等同于response.setHeader("Content-Type", "image/jpeg"); 51         //9.设置响应头控制浏览器不要缓存 52         response.setDateHeader("expries", -1); 53         response.setHeader("Cache-Control", "no-cache"); 54         response.setHeader("Pragma", "no-cache"); 55         //10.将图片写给浏览器 56         ImageIO.write(bi, "jpg", response.getOutputStream()); 57     } 58  59     /** 60      * 设置图片的背景色 61      * @param g 62      */ 63     private void setBackGround(Graphics g) { 64         // 设置颜色 65         g.setColor(Color.WHITE); 66         // 填充区域 67         g.fillRect(0, 0, WIDTH, HEIGHT); 68     } 69  70     /** 71      * 设置图片的边框 72      * @param g 73      */ 74     private void setBorder(Graphics g) { 75         // 设置边框颜色 76         g.setColor(Color.BLUE); 77         // 边框区域 78         g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2); 79     } 80  81     /** 82      * 在图片上画随机线条 83      * @param g 84      */ 85     private void drawRandomLine(Graphics g) { 86         // 设置颜色 87         g.setColor(Color.GREEN); 88         // 设置线条个数并画线 89         for (int i = 0; i < 5; i++) { 90             int x1 = new Random().nextInt(WIDTH); 91             int y1 = new Random().nextInt(HEIGHT); 92             int x2 = new Random().nextInt(WIDTH); 93             int y2 = new Random().nextInt(HEIGHT); 94             g.drawLine(x1, y1, x2, y2); 95         } 96     } 97  98     /** 99      * 画随机字符100      * @param g101      * @param createTypeFlag102      * @return103      * String... createTypeFlag是可变参数,104      * Java1.5增加了新特性:可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。注意:可变参数必须位于最后一项105      */106     private String drawRandomNum(Graphics2D g,String... createTypeFlag) {107         // 设置颜色108         g.setColor(Color.RED);109         // 设置字体110         g.setFont(new Font("宋体", Font.BOLD, 20));111         //常用的中国汉字112         String baseChineseChar = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";113         //数字和字母的组合114         String baseNumLetter = "0123456789ABCDEFGHJKLMNOPQRSTUVWXYZ";115         //纯数字116         String baseNum = "0123456789";117         //纯字母118         String baseLetter = "ABCDEFGHJKLMNOPQRSTUVWXYZ";119         //createTypeFlag[0]==null表示没有传递参数120         if (createTypeFlag.length > 0 && null != createTypeFlag[0]) {121             if (createTypeFlag[0].equals("ch")) {122                 // 截取汉字123                 return createRandomChar(g, baseChineseChar);124             }else if (createTypeFlag[0].equals("nl")) {125                 // 截取数字和字母的组合126                 return createRandomChar(g, baseNumLetter);127             }else if (createTypeFlag[0].equals("n")) {128                 // 截取数字129                 return createRandomChar(g, baseNum);130             }else if (createTypeFlag[0].equals("l")) {131                 // 截取字母132                 return createRandomChar(g, baseLetter);133             }134         }else {135             // 默认截取数字和字母的组合136             return createRandomChar(g, baseNumLetter);137         }138         139         return "";140     }141 142     /**143      * 创建随机字符144      * @param g145      * @param baseChar146      * @return 随机字符147      */148     private String createRandomChar(Graphics2D g,String baseChar) {149         StringBuffer sb = new StringBuffer();150         int x = 5;151         String ch ="";152         // 控制字数153         for (int i = 0; i < 4; i++) {154             // 设置字体旋转角度155             int degree = new Random().nextInt() % 30;156             ch = baseChar.charAt(new Random().nextInt(baseChar.length())) + "";157             sb.append(ch);158             // 正向角度159             g.rotate(degree * Math.PI / 180, x, 20);160             g.drawString(ch, x, 20);161             // 反向角度162             g.rotate(-degree * Math.PI / 180, x, 20);163             x += 30;164         }165         return sb.toString();166     }167 }
复制代码

运行结果如下:

  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