由于要做一個新項目,所以打算做一個簡單的圖片驗證碼。
先說說思路吧:在服務端,從一個文件夾里面找出8張圖片,再把8張圖片合并成一張大圖,在8個小圖里面隨機生成一個要用戶驗證的圖片分類,如小狗、啤酒等。在前端,訪問這個頁面時,把圖片加載上去,用戶在圖片上選擇提示所需要的圖片,當用戶點登陸時,根據用戶選擇的所有坐標判斷所選的圖片是不是實際上的驗證圖片。
先放兩張效果圖:
為了讓文件查找比較簡單,在圖片文件結構上可以這樣:
這樣方便生成用戶要選擇的Key圖片,和取出8張小圖合并成大圖。
上代碼:這是選擇8張圖片,并且在每張圖片選取時用遞歸保證選取的圖片不會重復。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
//選取8個圖片 public static List<Object> getEightImages() { //保存取到的每一個圖片的path,保證圖片不會重復 List<String> paths = new ArrayList<String>(); File[] finalImages = new File[ 8 ]; List<Object> object = new ArrayList<Object>(); //保存tips String[] tips = new String[ 8 ]; for ( int i = 0 ; i < 8 ; i++) { //獲取隨機的二級目錄 int dirIndex = getRandom(secondaryDirNumbers); File secondaryDir = getFiles()[dirIndex]; //隨機到的文件夾名稱保存到tips中 tips[i] = secondaryDir.getName(); //獲取二級圖片目錄下的文件 File[] images = secondaryDir.listFiles(); int imageIndex = getRandom(imageRandomIndex); File image = images[imageIndex]; //圖片去重 image = dropSameImage(image, paths, tips, i); paths.add(image.getPath()); finalImages[i] = image; } object.add(finalImages); object.add(tips); return object; } |
在生成這8張圖片中,用一個數組保存所有的文件分類。在這個分類里面可以用隨機數選取一個分類做為Key分類,就是用戶要選擇的所有圖片。由于數組是有序的,可以遍歷數組中的元素,獲取每個key分類圖片的位置,方便在用戶驗證時,進行匹配。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
//獲取位置,返回的是第幾個圖片,而不是下標,從1開始,集合第一個元素為tip public static List<Object> getLocation(String[] tips) { List<Object> locations = new ArrayList<Object>(); //獲取Key分類 String tip = getTip(tips); locations.add(tip); int length = tips.length; for ( int i = 0 ; i < length; i++) { if (tip.equals(tips[i])) { locations.add(i+ 1 ); } } return locations; } |
選取了8張圖片后,接下來就是合并圖片。合并圖片可以用到BufferedImage這個類的方法:setRGB()這個方法如果不明白可以看下api文檔,上面有詳細的說明。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
public static void mergeImage(File[] finalImages, HttpServletResponse response) throws IOException { //讀取圖片 BufferedImage mergeImage = new BufferedImage( 800 , 400 , BufferedImage.TYPE_INT_BGR); for ( int i = 0 ; i < 8 ; i++) { File image = finalImages[i]; BufferedImage bufferedImage = ImageIO.read(image); int width = bufferedImage.getWidth(); int height = bufferedImage.getHeight(); //從圖片中讀取RGB int [] imageBytes = new int [width*height]; imageBytes = bufferedImage.getRGB( 0 , 0 , width, height, imageBytes, 0 , width); if ( i < 4 ) { mergeImage.setRGB(i* 200 , 0 , width, height, imageBytes, 0 , width); } else { mergeImage.setRGB((i - 4 )* 200 , 200 , width, height, imageBytes, 0 , width); } } ImageIO.write(mergeImage, "jpg" , response.getOutputStream()); //ImageIO.write(mergeImage, "jpg", destImage); } |
在controller層中,先把key分類保存到session中,為用戶選擇圖片分類做提示和圖片驗證做判斷。然后把圖片流輸出到response中,就可以生成驗證圖片了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
response.setContentType( "image/jpeg" ); response.setHeader( "Pragma" , "No-cache" ); response.setHeader( "Cache-Control" , "no-cache" ); response.setDateHeader( "Expires" , 0 ); List<Object> object = ImageSelectedHelper.getEightImages(); File[] finalImages = (File[]) object.get( 0 ); String[] tips = (String[]) object.get( 1 ); //所有key的圖片位置,即用戶必須要選的圖片 List<Object> locations = ImageSelectedHelper.getLocation(tips); String tip = locations.get( 0 ).toString(); System.out.println(tip); session.setAttribute( "tip" , tip); locations.remove( 0 ); int length = locations.size(); for ( int i = 0 ; i < length; i++) { System.out.println( "實際Key圖片位置:" + locations.get(i)); } session.setAttribute( "locations" , locations); ImageMerge.mergeImage(finalImages, response); |
在jsp中,為用戶的點擊生成小圖片標記。當用戶點圖片擊時,在父div上添加一個子div標簽,并且把他定位為relative, 并且設置zIndex,然后再這個div上添加一個img標簽,定位為absolute。在用戶的點擊時,可以獲取點擊事件,根據點擊事件獲取點擊坐標,然后減去父div的坐標,就可以獲取相對坐標。可以根據自己的喜好定坐標原點,這里的坐標原點是第8個圖片的右下角。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
< div >< br > < div id = "base" >< br > < img src="<%=request.getContextPath()%>/identify" style="width: 300px; height: 150px;" onclick="clickImg(event)" id="bigPicture">< br > </ div >< br > < br > </ div >< br >< br >function clickImg(e) { var baseDiv = document.getElementById("base"); var topValue = 0; var leftValue = 0; var obj = baseDiv; while (obj) { leftValue += obj.offsetLeft; topValue +=obj.offsetTop; obj = obj.offsetParent; } //解決firefox獲取不到點擊事件的問題 var clickEvent = e ? e : (window.event ? window.event : null); var clickLeft = clickEvent.clientX + document.body.scrollLeft - document.body.clientLeft - 10; var clickTop = clickEvent.clientY + document.body.scrollTop - document.body.clientTop - 10; var divId = "img_" + index++; var divEle = document.createElement("div"); divEle.setAttribute("id", divId); divEle.style.position = "relative"; divEle.style.zIndex = index; divEle.style.width = "20px"; divEle.style.height = "20px"; divEle.style.display = "inline"; divEle.style.top = clickTop - topValue - 150 + 10 + "px"; divEle.style.left = clickLeft - leftValue - 300 + "px"; divEle.setAttribute("onclick", "remove('" + divId + "')"); baseDiv.appendChild(divEle); var imgEle = document.createElement("img"); imgEle.src = "<%=request.getContextPath()%>/resources/timo.png"; imgEle.style.width = "20px"; imgEle.style.height = "20px"; imgEle.style.top = "0px"; imgEle.style.left = "0px"; imgEle.style.position = "absolute"; imgEle.style.zIndex = index; divEle.appendChild(imgEle); } |
用戶選擇登錄后,服務器端根據用戶的選擇坐標進行判斷
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
public List<Integer> isPass(String result) { String[] xyLocations = result.split( "," ); //保存用戶選擇的坐標落在哪些圖片上 List<Integer> list = new ArrayList<Integer>(); //每一組坐標 System.out.println( "用戶選擇圖片數:" +xyLocations.length); for (String xyLocation : xyLocations) { String[] xy = xyLocation.split( "\\|\\|" ); int x = Integer.parseInt(xy[ 0 ]); int y = Integer.parseInt(xy[ 1 ]); //8,4圖片區間 if ( x > - 75 && x <= 0 ) { if ( y > - 75 && y <= 0 ) { //8號 list.add( 8 ); } else if ( y >= - 150 && y <= - 75 ) { //4號 list.add( 4 ); } } else if ( x > - 150 && x <= - 75 ) { //7,3圖片區間 if ( y > - 75 && y <= 0 ) { //7號 list.add( 7 ); } else if ( y >= - 150 && y <= - 75 ) { //3號 list.add( 3 ); } } else if ( x > - 225 && x <= - 150 ) { //6,2圖片區間 if ( y > - 75 && y <= 0 ) { //6號 list.add( 6 ); } else if ( y >= - 150 && y <= - 75 ) { //2號 list.add( 2 ); } } else if ( x >= - 300 && x <= - 225 ) { //5,1圖片區間 if ( y > - 75 && y <= 0 ) { //5號 list.add( 5 ); } else if ( y >= - 150 && y <= - 75 ) { //1號 list.add( 1 ); } } else { return null ; } } return list; } |
刷新生成新的圖片,由于ajax不支持二進制流,可以自己用原生的xmlHttpRequest對象加html5的blob來完成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
function refresh() { var url = "<%=request.getContextPath()%>/identify" ; var xhr = new XMLHttpRequest(); xhr.open( 'GET' , url, true ); xhr.responseType = "blob" ; xhr.onload = function() { if ( this .status == 200 ) { var blob = this .response; //加載成功后釋放blob bigPicture.onload = function(e) { window.URL.revokeObjectURL(bigPicture.src); }; bigPicture.src = window.URL.createObjectURL(blob); } } xhr.send(); |
驗證碼整體代碼完成了,還有有一些細節要處理。
以上就是本文的全部內容,希望對大家的學習有所幫助。