自己寫一個圖片驗證碼程序


本程序基於struts2,用action響應請求。

一、首先,創建一個用於產生隨即驗證碼圖片的類ImageCode.java.

  1 package com.exp.image;
  2 
  3 import java.awt.BasicStroke;
  4 import java.awt.Color;
  5 import java.awt.Font;
  6 import java.awt.Graphics;
  7 import java.awt.Graphics2D;
  8 import java.awt.RenderingHints;
  9 import java.awt.geom.AffineTransform;
 10 import java.awt.image.BufferedImage;
 11 import java.io.ByteArrayInputStream;
 12 import java.io.ByteArrayOutputStream;
 13 import java.io.File;
 14 import java.io.FileNotFoundException;
 15 import java.io.FileOutputStream;
 16 import java.io.IOException;
 17 import java.io.OutputStream;
 18 import java.util.Random;
 19 
 20 import javax.ejb.Init;
 21 import javax.imageio.ImageIO;
 22 import javax.imageio.stream.ImageOutputStream;
 23 
 24 public class ImageCode {
 25     private static Random random = new Random();// 隨機數對象
 26     private static ByteArrayInputStream inputStream;// 字節數組輸入流對象
 27     private static String randString;// 隨機生成的字符串
 28     private static ImageCode imageCode;// 本類對象
 29     // 以下是系統默認參數
 30     private static int nums = 6;// 驗證碼位數
 31     private static int width = 80;// 圖片寬
 32     private static int height = 22;// 圖片高
 33     private static int left = 4;// 字符輸出左邊距
 34     private static int top = 18;// 字符輸出上邊距
 35     private static int fontSize = 22;// 字符輸出上邊距
 36     private static String charSet = "0123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ";// 驗證碼字符集合
 37     private static int stringMinColor = 100;// 字符最低顏色值
 38     private static int stringMaxColor = 177;// 字符最高顏色值
 39     private static int otherMinColor = 178;// 干擾線和噪點最低顏色值
 40     private static int otherMaxColor = 255;// 干擾線和噪點最高顏色值
 41     private static Color backgroundColor = Color.cyan;// 圖片背景色
 42     private static boolean isShear = false;// 是否進行字符扭曲
 43 
 44     /**
 45      * 單例模式
 46      */
 47     static {
 48         if (imageCode == null) {
 49             imageCode = new ImageCode();
 50         }
 51     }
 52 
 53     /**
 54      * 設置綜合信息
 55      * 
 56      * @param nums
 57      *            驗證碼位數
 58      * @param width
 59      *            圖片寬度,默認值80
 60      * @param height
 61      *            圖片高度,默認值22
 62      * @param left
 63      *            字符輸出左邊距,默認4
 64      * @param top
 65      *            字符輸出上邊距,默認20
 66      * @param fontSize
 67      *            字符大小,默認22
 68      * @param charSet
 69      *            驗證碼字符集合字符串,默認為
 70      *            "0123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
 71      * @param stringMinColor
 72      *            字符輸出最小顏色值,默認100
 73      * @param stringMaxColor
 74      *            字符輸出最大顏色值,默認177
 75      * @param otherMinColor
 76      *            干擾線和噪點最小顏色值,默認178
 77      * @param otherMaxColor
 78      *            干擾線和噪點最大顏色值,默認255
 79      * @param backgroundColor
 80      *            圖片背景色,默認Color.cyan
 81      * @param isShear
 82      *            是否進行單個字符扭曲,默認false
 83      */
 84     public static void set(int nums, int width, int height, int left, int top,
 85             int fontSize, String charSet, int stringMinColor,
 86             int stringMaxColor, int otherMinColor, int otherMaxColor,
 87             Color backgroundColor,boolean isShear) {
 88         // 設置驗證碼位數
 89         if (nums != 0) {
 90             ImageCode.nums = nums;
 91         }
 92         // 設置驗證碼圖片寬度
 93         if (width != 0) {
 94             ImageCode.width = width;
 95         }
 96         // 設置驗證碼圖片高度
 97         if (height != 0) {
 98             ImageCode.height = height;
 99         }
100         // 設置驗證碼字符輸出左邊距
101         if (left != 0) {
102             ImageCode.left = left;
103         }
104         // 設置驗證碼字符輸出上邊距
105         if (top != 0) {
106             ImageCode.top = top;
107         }
108         // 設置驗證碼字符大小
109         if (fontSize != 0) {
110             ImageCode.fontSize = fontSize;
111         }
112         // 設置驗證碼字符集合字符串
113         if (charSet != null) {
114             ImageCode.charSet = charSet;
115         }
116         // 設置驗證碼字符輸出最小顏色值
117         if (stringMinColor != 0) {
118             ImageCode.stringMinColor = stringMinColor;
119         }
120         // 設置驗證碼字符輸出最大顏色值
121         if (stringMaxColor != 0) {
122             ImageCode.stringMaxColor = stringMaxColor;
123         }
124         // 設置驗證碼干擾線和噪點最小顏色值
125         if (otherMinColor != 0) {
126             ImageCode.otherMinColor = otherMinColor;
127         }
128         // 設置驗證碼干擾線和噪點最大顏色值
129         if (otherMaxColor != 0) {
130             ImageCode.otherMaxColor = otherMaxColor;
131         }
132         // 設置驗證碼圖片背景色
133         if (backgroundColor != null) {
134             ImageCode.backgroundColor = backgroundColor;
135         }
136         // 設置驗證碼圖片背景色
137             ImageCode.isShear = isShear;
138     }
139 
140     /**
141      * 創建隨機圖片,生成隨機字符串
142      */
143     public static void createRandomImage() {
144         // 建立內存圖像
145         BufferedImage image = new BufferedImage(width, height,
146                 BufferedImage.TYPE_INT_RGB);
147         // 創建圖形上下文
148         Graphics2D g = image.createGraphics();
149         // 消除線段的鋸齒邊緣
150         g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
151                 RenderingHints.VALUE_ANTIALIAS_ON);
152         // 設置底色
153         g.setColor(Color.PINK);
154         // 繪制填充矩形
155         g.fillRect(0, 0, width, height);
156         // 繪制干擾線
157         for (int i = 0; i < 200; i++) {
158             g.setColor(getRandColor(otherMinColor, otherMaxColor));
159             g.setStroke(new BasicStroke(1f));
160             int x1 = random.nextInt(width);
161             int y1 = random.nextInt(height);
162             int x2 = random.nextInt(width * 2);
163             int y2 = random.nextInt(height * 2);
164             g.drawLine(x1, y1, x2, y2);
165         }
166 
167         // 獲得字符串
168         StringBuffer sBuffer = new StringBuffer();
169 
170         // 按位數拼接隨機字符串
171         for (int i = 0; i < nums; i++) {
172             // 根據隨機數在charSet中尋找字符
173             sBuffer.append(charSet.charAt(random.nextInt(charSet.length())));
174         }
175         String code = sBuffer.toString();// 這個字符串是包含大小寫字母的,不包括Oo
176         setRandString(code.toLowerCase());// 所有字母轉小寫
177 
178         // 繪制字符串
179         if (isShear) {
180             // 字符逐個扭曲輸出
181             shearEveryChar(g, code, 0, 127);
182         } else {
183             // 輸出字符串,這是不進行單個字符扭曲的選擇
184             g.setColor(getRandColor(stringMinColor, stringMaxColor));
185             g.setFont(new Font("黑體", Font.BOLD + Font.ITALIC, 22));
186             g.drawString(code, left, top);
187         }
188 
189         // 繪制噪點
190         drawNoise(g, image, 0.05f);
191 
192         // 扭曲整個圖片
193         // shear(g, width, height, getRandColor(128, 255));// 使圖片扭曲
194 
195         // 設置輸出流
196         ByteArrayOutputStream bos = new ByteArrayOutputStream();
197         try {
198             ImageOutputStream ios = ImageIO.createImageOutputStream(bos);
199             ImageIO.write(image, "jpg", ios);
200             ByteArrayInputStream bis = new ByteArrayInputStream(
201                     bos.toByteArray());
202             setInputStream(bis);
203             bos.flush();
204             bis.close();
205             ios.close();
206         } catch (IOException e) {
207             e.printStackTrace();
208         } finally {
209             try {
210                 bos.flush();
211                 bos.close();
212             } catch (IOException e) {
213                 e.printStackTrace();
214             }
215         }
216 
217     }
218 
219     /**
220      * 繪制噪點
221      * 
222      * @param g
223      * @param image
224      * @param yRote
225      */
226     public static void drawNoise(Graphics2D g, BufferedImage image, float yRote) {
227         int area = (int) (yRote * width * height);
228         for (int i = 0; i < area; i++) {
229             g.setStroke(new BasicStroke(2f));
230             int x = random.nextInt(width);
231             int y = random.nextInt(height);
232             int rgb = getRandomIntColor();
233             image.setRGB(x, y, rgb);
234         }
235     }
236 
237     /**
238      * 字符逐個扭曲輸出
239      * 
240      * @param g
241      * @param code
242      */
243     public static void shearEveryChar(Graphics2D g, String code,
244             int stringMinColor, int stringMaxColor) {
245         int fontSize = height - 4;
246         Font font = new Font("黑體", Font.BOLD + Font.ITALIC, fontSize);
247         g.setFont(font);
248         char[] chars = code.toCharArray();
249         for (int i = 0; i < code.length(); i++) {
250             g.setColor(getRandColor(stringMinColor, stringMaxColor));
251             AffineTransform affine = new AffineTransform();
252             affine.setToRotation(
253                     Math.PI / 4 * random.nextDouble()
254                             * (random.nextBoolean() ? 1 : -1),
255                     (width / code.length()) * i + fontSize / 2, height / 2);
256             g.setTransform(affine);
257             g.drawChars(chars, i, 1, ((width - 10) / code.length()) * i + 5,
258                     height / 2 + fontSize / 2 - 3);
259         }
260     }
261 
262     /**
263      * 獲得隨機顏色
264      * 
265      * @param fc
266      *            顏色起始值
267      * @param bc
268      *            顏色終止值
269      * @return 返回Color對象
270      */
271     private static Color getRandColor(int fc, int bc) {
272         if (fc > 255)
273             fc = 255;
274         if (bc > 255)
275             bc = 255;
276         int r = fc + random.nextInt(bc - fc);
277         int g = fc + random.nextInt(bc - fc);
278         int b = fc + random.nextInt(bc - fc);
279         return new Color(r, g, b);
280     }
281 
282     /**
283      * 獲得整型隨機色
284      * 
285      * @return
286      */
287     private static int getRandomIntColor() {
288         int[] rgb = getRandomRgb();
289         int color = 0;
290         for (int c : rgb) {
291             color = color << 8;
292             color = color | c;
293         }
294         return color;
295     }
296 
297     /**
298      * 獲得隨機數組,適配整數類型的顏色值
299      * 
300      * @return
301      */
302     private static int[] getRandomRgb() {
303         int[] rgb = new int[3];
304         for (int i = 0; i < 3; i++) {
305             rgb[i] = random.nextInt(255);
306         }
307         return rgb;
308     }
309 
310     // 整個圖片扭曲
311     private static void shear(Graphics g, int w1, int h1, Color color) {
312         shearX(g, w1, h1, color);
313         shearY(g, w1, h1, color);
314     }
315 
316     // X軸扭曲
317     private static void shearX(Graphics g, int w1, int h1, Color color) {
318         int period = random.nextInt(2);
319         boolean borderGap = true;
320         int frames = 1;
321         int phase = random.nextInt(2);
322 
323         for (int i = 0; i < h1; i++) {
324             double d = (double) (period >> 1)
325                     * Math.sin((double) i / (double) period
326                             + (6.2831853071795862D * (double) phase)
327                             / (double) frames);
328             g.copyArea(0, i, w1, 1, (int) d, 0);
329             if (borderGap) {
330                 g.setColor(color);
331                 g.drawLine((int) d, i, 0, i);
332                 g.drawLine((int) d + w1, i, w1, i);
333             }
334         }
335 
336     }
337 
338     // Y軸扭曲
339     private static void shearY(Graphics g, int w1, int h1, Color color) {
340         int period = random.nextInt(40) + 10; // 50;
341         boolean borderGap = true;
342         int frames = 20;
343         int phase = 7;
344         for (int i = 0; i < w1; i++) {
345             double d = (double) (period >> 1)
346                     * Math.sin((double) i / (double) period
347                             + (6.2831853071795862D * (double) phase)
348                             / (double) frames);
349             g.copyArea(i, 0, 1, h1, 0, (int) d);
350             if (borderGap) {
351                 g.setColor(color);
352                 g.drawLine(i, (int) d, i, 0);
353                 g.drawLine(i, (int) d + h1, i, h1);
354             }
355 
356         }
357 
358     }
359 
360     // 封裝
361     public static ByteArrayInputStream getInputStream() {
362         return inputStream;
363     }
364 
365     public static void setInputStream(ByteArrayInputStream inputStream) {
366         ImageCode.inputStream = inputStream;
367     }
368 
369     public static String getRandString() {
370         return randString;
371     }
372 
373     public static int getNums() {
374         return nums;
375     }
376 
377     public static void setNums(int nums) {
378         ImageCode.nums = nums;
379     }
380 
381     public static int getWidth() {
382         return width;
383     }
384 
385     public static void setWidth(int width) {
386         ImageCode.width = width;
387     }
388 
389     public static int getHeight() {
390         return height;
391     }
392 
393     public static void setHeight(int height) {
394         ImageCode.height = height;
395     }
396 
397     public static int getLeft() {
398         return left;
399     }
400 
401     public static void setLeft(int left) {
402         ImageCode.left = left;
403     }
404 
405     public static int getTop() {
406         return top;
407     }
408 
409     public static void setTop(int top) {
410         ImageCode.top = top;
411     }
412 
413     public static String getCharSet() {
414         return charSet;
415     }
416 
417     public static void setCharSet(String charSet) {
418         ImageCode.charSet = charSet;
419     }
420 
421     public static int getStringMinColor() {
422         return stringMinColor;
423     }
424 
425     public static void setStringMinColor(int stringMinColor) {
426         ImageCode.stringMinColor = stringMinColor;
427     }
428 
429     public static int getStringMaxColor() {
430         return stringMaxColor;
431     }
432 
433     public static void setStringMaxColor(int stringMaxColor) {
434         ImageCode.stringMaxColor = stringMaxColor;
435     }
436 
437     public static int getOtherMinColor() {
438         return otherMinColor;
439     }
440 
441     public static void setOtherMinColor(int otherMinColor) {
442         ImageCode.otherMinColor = otherMinColor;
443     }
444 
445     public static int getOtherMaxColor() {
446         return otherMaxColor;
447     }
448 
449     public static void setOtherMaxColor(int otherMaxColor) {
450         ImageCode.otherMaxColor = otherMaxColor;
451     }
452 
453     public static Color getBackgroundColor() {
454         return backgroundColor;
455     }
456 
457     public static void setBackgroundColor(Color backgroundColor) {
458         ImageCode.backgroundColor = backgroundColor;
459     }
460 
461     public static ImageCode getImageCode() {
462         return imageCode;
463     }
464 
465     public static void setImageCode(ImageCode imageCode) {
466         ImageCode.imageCode = imageCode;
467     }
468 
469     public static void setRandString(String randString) {
470         ImageCode.randString = randString;
471     }
472 
473     public static int getFontSize() {
474         return fontSize;
475     }
476 
477     public static void setFontSize(int fontSize) {
478         ImageCode.fontSize = fontSize;
479     }
480 
481     public static boolean isShear() {
482         return isShear;
483     }
484 
485     public static void setShear(boolean isShear) {
486         ImageCode.isShear = isShear;
487     }
488 
489 }

基本思路:

1.用字符集合確定驗證碼顯示的字符;

2.用參數確定各種控制數據;

3.生成一個字符串,賦予一個成員變量,用這個字符串循環繪制在畫布上,給圖片添加干擾線、噪點和文字扭曲等效果;

4.圖片最終賦給一個字節數組輸入流對象。

二、創建TestAction.java

 1 package com.exp.action;
 2 
 3 import java.io.ByteArrayInputStream;
 4 
 5 import javax.servlet.http.HttpServletResponse;
 6 
 7 import com.exp.image.ImageCode;
 8 import com.opensymphony.xwork2.ActionContext;
 9 
10 public class TestAction {
11     private String usercode;// 用戶輸入的驗證碼
12     private ByteArrayInputStream inputStream;// 圖片輸入流
13     private String info;
14 
15     /**
16      * 登錄
17      * 
18      * @return
19      */
20     public String login() {
21         // 輸出測試
22         System.out.println(ImageCode.getRandString());
23         if (ImageCode.getRandString().equals(usercode.toLowerCase())) {
24             System.out.println("沒問題");
25             info = "驗證碼輸入正確";
26         } else {
27             System.out.println("不對");
28             info = "驗證碼輸入不正確";
29         }
30         return "test_login_success";
31     }
32 
33     /**
34      * 獲取圖片驗證碼
35      * 
36      * @return
37      */
38     public String getimagecode() {
39         // 創建圖片驗證碼
40         ImageCode.createRandomImage();
41         ImageCode.setHeight(21);// 設置高度
42         // 輸出測試
43         System.out.println(ImageCode.getRandString());
44         // 將圖片輸入自字節流賦予inputStream
45         this.setInputStream(ImageCode.getInputStream());
46         return "test_getimagecode_success";
47     }
48 
49     // 封裝
50     public String getUsercode() {
51         return usercode;
52     }
53 
54     public void setUsercode(String usercode) {
55         this.usercode = usercode;
56     }
57 
58     public ByteArrayInputStream getInputStream() {
59         return inputStream;
60     }
61 
62     public void setInputStream(ByteArrayInputStream inputStream) {
63         this.inputStream = inputStream;
64     }
65 
66     public String getInfo() {
67         return info;
68     }
69 
70     public void setInfo(String info) {
71         this.info = info;
72     }
73 }

思路:使用單例模式,獲取驗證碼字節數組輸入流對象,並且聲稱驗證碼字符串。

三、配置struts.xml

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
 3 <struts>
 4     <package name="test" namespace="/" extends="struts-default">
 5         <action name="*_*" class="com.exp.action.{1}Action" method="{2}">
 6         <result name="test_login_success">/index2.jsp</result>
 7             <!-- 圖片驗證碼 -->
 8             <result name="test_getimagecode_success" type="stream">        
 9                 <param name="contentType">image/jpeg</param>        
10                 <param name="inputName">inputStream</param>        
11             </result>  
12         </action>
13     </package>
14 </struts>    

思路:規定結果類型為流,設定參數類型為圖片,設定inputName的值為TestAction中的字節數組輸入流對象。

四、設計測試頁面index2.jsp

 1 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
 2 <%@ taglib uri="/struts-tags" prefix="s" %>
 3 <%
 4     String path = request.getContextPath();
 5     String basePath = request.getScheme() + "://"
 6             + request.getServerName() + ":" + request.getServerPort()
 7             + path + "/";
 8 %>
 9 
10 <!DOCTYPE html>
11 <html>
12 <head>
13 <base href="<%=basePath%>">
14 <title>My JSP 'index.jsp' starting page</title>
15 <script type="text/javascript" src="js/jquery-2.1.4.js"></script>
16 </head>
17 
18 <body>
19     <form action="Test_login" method="post">
20     <input id="othertext" placeholder="驗證異步刷新" style="float:left;">
21         <input name="usercode" maxlength="6" style="float:left;"> <span style="display:block;float:left;width:80px;height:22px;border:0px solid green;"><img
22             id="randimg" src="Test_getimagecode" onclick="changeRandomImage();" style="border:0px solid red;"></span><br>
23         <input type="submit" value="OK" style="width:100px;height:21px;float:left;margin:-19px 0 0 10px;">
24     </form>
25     <div style="width:200px;height:22px;float:left;margin:-18px 0 0 10px;color:red;"><s:property value="info"/></div>
26     
27     <script type="text/javascript">
28     //異步刷新圖片驗證碼
29         function changeRandomImage() {
30             var imgSrc = $("#randimg");
31             var src = imgSrc.attr("src");
32             imgSrc.attr("src", changeGetImageUrl(src));
33         }
34         //時間戳
35         //為了使每次生成圖片不一致,即不讓瀏覽器讀緩存,所以需要加上時間戳,讓每次都有新的請求
36         function changeGetImageUrl(url) {
37             var timestamp = (new Date()).valueOf();
38             if ((url.indexOf("&") >= 0)) {
39                 url = url + "×tamp=" + timestamp;
40             } else {
41                 url = url + "?timestamp=" + timestamp;
42             }
43             return url;
44         }
45     </script>
46 </body>
47 </html>

思路:點擊圖片進行異步刷新。為了防止每次刷新時頁面讀取緩存導致不變,特意設置一個沒有實際作用的時間戳為參數形成不同的請求,獲得新的結果

五、運行效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM