Java、Servlet生成驗證碼由淺到深

不BB,先上原理性的代碼(基本能力強的看代碼就理解了):

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        final int WIDTH = 100;      //驗證碼的寬度
        final int HEIGHT = 30;      //驗證碼的高度
        final int SIZE = 25;        //字體的大小
        final int LENGTH = 4;       //驗證碼的個數

        //所有可能當驗證碼的字符(去掉了數字1和字母小l,防止辨識錯誤)
        char[] chs = "023456789abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
        Random random = new Random();       //隨機類,用以生成字符串數組的下標,以此來選中驗證碼字符
        //新建一張圖片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics grap = image.getGraphics();    //獲取圖形,以便進行編輯

        grap.setColor(Color.LIGHT_GRAY);        //拿起哪種顏料
        grap.fillRect(0, 0, WIDTH, HEIGHT);     //使用拿起的顏料給圖形背景上色

        for (int i=0; i<LENGTH; i++) {      //生成隨機驗證碼並畫到圖形上
            int pos = random.nextInt(chs.length);
            Font font = new Font("宋體", Font.BOLD, SIZE);        //字體對象
            grap.setFont(font);     //設置字體屬性
            grap.setColor(Color.MAGENTA);  //設置字體的顏色
            grap.drawString(chs[pos] + "", (i*SIZE)+4, 24);     //在(x, y)處畫出指定字符
        }
        try {
            //以圖片的形式在E:\checkcode.jpg處存放驗證碼
            ImageIO.write(image, "JPG", new File("E:/checkcode.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

上面是我參考多篇博客簡化的原理性代碼,方便理解能力強的直接get要點,
然後以下就是我自己進階的過程(其中關於Servlet的在最後面並顯示到網頁上的在最後面),不太建議大家看,因爲排版不太友好。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
這些天
在寫一個小項目準備找實習
但是卻在生成驗證碼這塊卡住了
在網上看了許多的博客
基本都是清一色的一大串代碼然後加幾行註釋
看得有些雲裏霧裏不知從何下手
然後就寫了這篇博客
邊寫邊學習,在自己會的同時也希望帶動更多的需要者更容易的入手
這也算是貫徹落實了我國的方針政策——“先懂帶動後懂”

零、實現驗證碼的主要思想

1. 將可以當驗證碼的字符存到一個字符串數組裏面,隨機生成下標來選擇驗證碼字符
2. 使用java.util.Random類的nextInt(int bound)來生成[0, bound)區間內的整數,當下標
3. 使用java.awt.image.BufferedImage來生成一張圖片
4. 使用java.awt.Graphics來編輯圖片,即在上面畫驗證碼
5. 最後一步是用javax.imageio.ImageIO的write()方法將驗證碼圖片存儲到或者發送到指定的地方

一、簡化版驗證碼的生成

具體思路已經在上面講了,所謂簡化版,就是指去掉大部的修飾和應用背景然後單純地實現一個簡單可見的圖片
首先,先上個效果圖,激發一下大家的鬥志,哈哈哈
這裏寫圖片描述
一些類和方法的介紹如下(基礎好的可以直接看源碼,源碼有解釋,容易理解):

  • BufferedImage類是一個存儲圖片的類,可以簡單的理解它的一個對象就是一張圖片。它的其中一個構造函數有三個參數,第一個是圖片的寬度,第二個是圖片的高度,第三個,雖然有點羞澀,但是我還是大方地承認了,我也不懂,我只是單純地使用了和別人一樣的參數——BufferedImage.TYPE_INT_RGB。這個類有一個無參數方法getGraphics(),是用來返回一個可編輯的圖形Graphics對象的。
  • Graphics類,簡單的理解爲圖形類,在圖形上面,我們可以畫上我們的驗證碼。我們使用它的drawString()方法來將我們隨機生成的驗證碼字符畫到上面,這個方法有三個參數,第一個是一個String類型,即我們需要畫到這個圖形上的字符,第二、第三個參數爲座標(x,y),用來指定從哪裏開始畫給定的String。除了這個方法還有許多我們可能用到的方法如設置字體、顏色等,這裏就不一一介紹了,讀者應該看一眼源碼就能明白。
  • Random類是用來生成隨機數的,裏面有一個方法nextInt(int bound),用來返回一個在區間[0, bound)內的整數。我們使用這個類來生成一個整數,這個整數是一個下標,用來定位驗證碼字符。
  • ImageIO類,我很無恥地承認了吧,我也不懂用來幹嘛的,我就用過它的一個static方法write(),這個方法有其中一個形態有三個參數,第一個是BufferedImage的對象,第二個是圖片的格式如jpg\png等,String類型的,第三個參數是一個位置吧,用來顯示第一個參數的,這裏使用的是new File(“E:/checkcode.jpg”),很明顯,是將BufferedImage的對象以圖片的形式放在操作系統的E盤下。

以下是具體的代碼,結合註解和前面的介紹,相信不難理解

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        final int WIDTH = 100;      //驗證碼的寬度
        final int HEIGHT = 30;      //驗證碼的高度
        final int SIZE = 25;        //字體的大小
        final int LENGTH = 4;       //驗證碼的個數

        //所有可能當驗證碼的字符(去掉了數字1和字母小l,防止辨識錯誤)
        char[] chs = "023456789abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
        Random random = new Random();       //隨機類,用以生成字符串數組的下標,以此來選中驗證碼字符
        //新建一張圖片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics grap = image.getGraphics();    //獲取圖形,以便進行編輯

        grap.setColor(Color.LIGHT_GRAY);        //拿起哪種顏料
        grap.fillRect(0, 0, WIDTH, HEIGHT);     //使用拿起的顏料給圖形背景上色

        for (int i=0; i<LENGTH; i++) {      //生成隨機驗證碼並畫到圖形上
            int pos = random.nextInt(chs.length);
            Font font = new Font("宋體", Font.BOLD, SIZE);        //字體對象
            grap.setFont(font);     //設置字體屬性
            grap.setColor(Color.MAGENTA);  //設置字體的顏色
            grap.drawString(chs[pos] + "", (i*SIZE)+4, 24);     //在(x, y)處畫出指定字符
        }
        try {
            //以圖片的形式在E:\checkcode.jpg處存放驗證碼
            ImageIO.write(image, "JPG", new File("E:/checkcode.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

二、進階版驗證碼的生成

如果你看懂了上面的代碼,並且實現了出來,那麼恭喜你
你學到的這些並沒有什麼卵用
(⊙o⊙)…當然是開玩笑了,別緊張別緊張,哈哈哈
上面那個簡化版是一個基礎,有了這個基礎,再在這個基礎之上進一步修飾
我們就能畫出我們很中意的驗證碼圖片了
想想是不是很激動,我也很激動,因爲這一部分我也不會,正在學習…
咳咳,無敵的我又回來了
關於這個進階,就加了一些干擾線還有旋轉
效果如下
這裏寫圖片描述
具體用到的方法和類介紹如下

  • 字符的旋轉使用的是Graphics2D類的rotate()方法,這個方法有三個參數,第一個是雖然不太懂,但是估摸着是和旋轉的角度弧度有關,實踐證明我是對的,第二個參數和第三個參數給出旋轉字符的座標。代碼裏面主要寫法如下((Graphics2D)grap).rotate(angle*Math.PI/180, x, y);。
  • 干擾線的顯示使用的是Graphics類的drawLine()方法,這個方法有四個參數,順序給出兩個二維座標,根據這兩個座標,即可畫出一條幹擾線。

以下是上次簡化版的代碼升級之後的代碼

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

public class Test {
    public static void main(String[] args) {
        String checkCode = drawCheckCode();
        System.out.println(checkCode);
    }

    public static String drawCheckCode() {
        /*如果要改變驗證碼的屬性
         *可以更改WIDTH、HEIGHT、SIZE、LENGTH、LINENUM
         * (x, y)座標
         * 旋轉角度angle
         * 干擾線的值
         * 以上這些基本就可以
         */
        final int WIDTH = 100;      //驗證碼的寬度
        final int HEIGHT = 30;      //驗證碼的高度
        final int SIZE = 25;        //字體的大小
        final int LENGTH = 4;       //驗證碼的個數
        final int LINENUM = 5;      //干擾線的條數

        char[] str = new char[LENGTH];
        //所有可能當驗證碼的字符(去掉了數字1和字母小l,還有數字0和字母大O和字母小o,防止辨識錯誤)
        char[] chs = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ".toCharArray();
        Random random = new Random();       //隨機類,用以生成字符串數組的下標,以此來選中驗證碼字符
        //新建一張圖片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics grap = image.getGraphics();    //獲取圖形,以便進行編輯

        grap.setColor(Color.LIGHT_GRAY);        //拿起哪種顏料
        grap.fillRect(0, 0, WIDTH, HEIGHT);     //使用拿起的顏料給圖形背景上色

        for (int i=0; i<LENGTH; i++) {      //生成隨機驗證碼並畫到圖形上
            int pos = random.nextInt(chs.length);
            int angle = random.nextInt() % 40;
            int x = (i*SIZE)+5;
            int y = 24;

            str[i] = chs[pos];      //記錄驗證碼

            Font font = new Font("宋體", Font.BOLD, SIZE);        //字體對象
            grap.setFont(font);     //設置字體屬性
            ((Graphics2D)grap).rotate(angle*Math.PI/180, x, y);   //旋轉
            grap.setColor(Color.MAGENTA);  //設置字體的顏色
            grap.drawString(chs[pos] + "", x, y);     //在(x, y)處畫出指定字符
            ((Graphics2D)grap).rotate(-angle*Math.PI/180, x, y);  //轉回來
        }

        //畫干擾線
        for (int i=0; i<LINENUM; i++) {
            int rgb = random.nextInt(256);
            int x1 = random.nextInt(WIDTH);
            int y1 = random.nextInt(HEIGHT);
            int x2 = random.nextInt(WIDTH);
            int y2 = random.nextInt(HEIGHT);
            grap.setColor(new Color(rgb));
            grap.drawLine(x1, y1, x2, y2);      //(x1, y1)到(x2, y2)的線段
        }

        String imageFormat = "jpg";
        try {
            ImageIO.write(image, imageFormat, new File("E:/checkcode." + imageFormat));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(str);
    }
}

三、實用版驗證碼的生成(Servlet和HTML)

相信能堅持看到這裏的人,都是會部署Servlet的人,
所以關於Servlet的基礎配置我就不講了,主要講在前面代碼的基礎之上所添加或者修改的東西,
首先,按照國際慣例,我們先講一下大體的思路

  1. 我們需要兩個代碼文件,一個是生成驗證碼圖片並且發送給客戶端的Servlet類,一個是接收驗證碼圖片的HTML。
  2. 生成驗證碼圖片的Servlet類,需要將生成的圖片以流的形式傳出去,誰訪問這個類的地址,誰就能獲取到驗證碼圖片。一般是使用形如ImageIO.write(image, "jpg", res.getOutputStream());的代碼進行圖片流的傳送。
  3. 比如這個Servlet類你給定的URL地址爲/login/create,你只需要在客戶端設置一個<img id='checkimg' src='/login/create' alt='無法顯示' />這個標籤,src屬性指向Servlet類的URL地址,即可接收驗證碼圖片。這就是那個HTML文件的功能。
  4. 然後我認爲其中一個技術難點在於怎麼在點擊驗證碼圖片的時候,刷新驗證碼。這個我主要靠更改<img>標籤的屬性src的值達到的,因爲更改前和更改後的src如果一樣,瀏覽器是不會重新發送請求的,所以我每次使用的src屬性最後面都會加一個new Date(),因爲參數不一樣了,所以就能請求了。如 src = ‘/login/create?’ + new Date()。

具體代碼如下(生成驗證碼的代碼主要改了ImageIO.write()那裏的第三個參數):

CreateCheckCode.java
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

public class CreateCheckCode extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res) {
        res.setContentType("image/jpg");
        HttpSession session = req.getSession();
        String checkCode = drawCheckCode(res);
        session.setAttribute("checkCode", checkCode);
    }

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse res) {
        doGet(req, res);
    }

    //生成驗證碼
    public String drawCheckCode(HttpServletResponse res) {
        /*如果要改變驗證碼的屬性
         *可以更改WIDTH、HEIGHT、SIZE、LENGTH、LINENUM
         * (x, y)座標
         * 旋轉角度angle
         * 干擾線的值
         * 以上這些基本就可以
         */
        final int WIDTH = 100;      //驗證碼的寬度
        final int HEIGHT = 30;      //驗證碼的高度
        final int SIZE = 25;        //字體的大小
        final int LENGTH = 4;       //驗證碼的個數
        final int LINENUM = 5;      //干擾線的條數

        char[] str = new char[LENGTH];
        //所有可能當驗證碼的字符(去掉了數字1和字母小l,還有數字0和字母大O和字母小o,防止辨識錯誤)
        char[] chs = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ".toCharArray();
        Random random = new Random();       //隨機類,用以生成字符串數組的下標,以此來選中驗證碼字符
        //新建一張圖片
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        Graphics grap = image.getGraphics();    //獲取圖形,以便進行編輯

        grap.setColor(Color.LIGHT_GRAY);        //拿起哪種顏料
        grap.fillRect(0, 0, WIDTH, HEIGHT);     //使用拿起的顏料給圖形背景上色

        for (int i=0; i<LENGTH; i++) {      //生成隨機驗證碼並畫到圖形上
            int pos = random.nextInt(chs.length);
            int angle = random.nextInt() % 40;
            int x = (i*SIZE)+5;
            int y = 24;

            str[i] = chs[pos];      //記錄驗證碼

            Font font = new Font("宋體", Font.BOLD, SIZE);        //字體對象
            grap.setFont(font);     //設置字體屬性
            ((Graphics2D)grap).rotate(angle*Math.PI/180, x, y);   //旋轉
            grap.setColor(Color.MAGENTA);  //設置字體的顏色
            grap.drawString(chs[pos] + "", x, y);     //在(x, y)處畫出指定字符
            ((Graphics2D)grap).rotate(-angle*Math.PI/180, x, y);  //轉回來
        }

        //畫干擾線
        for (int i=0; i<LINENUM; i++) {
            int rgb = random.nextInt(256);
            int x1 = random.nextInt(WIDTH);
            int y1 = random.nextInt(HEIGHT);
            int x2 = random.nextInt(WIDTH);
            int y2 = random.nextInt(HEIGHT);
            grap.setColor(new Color(rgb));
            grap.drawLine(x1, y1, x2, y2);      //(x1, y1)到(x2, y2)的線段
        }

        String imageFormat = "jpg";
        try {
            ImageIO.write(image, imageFormat, res.getOutputStream());
        } catch (IOException e) {
            System.out.println("IOException...");
            //e.printStackTrace();
        }
        return new String(str);
    }
}

HTML代碼如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test</title>
    <script type="text/javascript">
        function changeCheckimg(context) {
            //最後那個new Date()可以看做參數,因爲只有src發生了改變,瀏覽器纔會重新請求
            context.src = "/login/create?" + new Date();
        }
    </script>
</head>
<body>
<img id='checkimg' src='/login/create' alt='無法顯示' onclick="javascript:changeCheckimg(this);" />
</body>
</html>

至此,關於生成驗證碼的知識全部搞定,加上寫這個博客,用了兩天,
自己真笨,居然用了這麼長的時間,再貼一下效果圖,讓自己高興一下,感覺自己傻逼傻逼棒棒的
這裏寫圖片描述
關於這個Servlet生成驗證碼圖片並且發送,我再囉嗦一兩點

  • 主要使用BufferedImage類和Graphics類,ImageIO.write()靜態方法和Random類
  • 在Servlet使用流來發送圖片,這就使用到了response.getOutputStream()
  • 在瀏覽器顯示圖片的時候,使用改變<img>標籤的src屬性達到點擊刷新的功能
    呼…
    舒服…
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章