java生成圖形驗證碼詳細教程 帶註釋 拷貝即用 兼容linux系統

先看效果

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
比較難看清 因爲我用了些花裏胡哨的字體,用傳統點的字體還是比較好分辨的
在這裏插入圖片描述
demo地址(每一次打開都不一樣 該鏈接隨時可能關閉)

1.準備字體

在這裏插入圖片描述
上圖用的是這幾種字體 由於是商用字體涉及到版權問題這裏就不提供下載了
本項目用的是以下兩種字體
在這裏插入圖片描述
這兩種字體相信大家都有吧
沒有的童鞋打開 控制面板-搜索字體-點這個-把裏面的字體複製出來就行了
在這裏插入圖片描述
在這裏插入圖片描述

2.創建項目

創建一個普通的java項目即可 這裏用的是idea、jdk1.8.0_144
在這裏插入圖片描述
在項目根目錄創建一個fonts文件夾
把剛剛拿到的字體丟到文件夾裏
在這裏插入圖片描述
創建好項目之後 開始創建我們的第一個類
在這裏插入圖片描述

3.創建類VerifyImageCodeUtils

該類用來生成驗證碼

package com.liziguo.utils;

import sun.font.FontDesignMetrics;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Random;

/**
 * @Author:Liziguo
 * @Date:2019/12/11 23:45
 */
public class VerifyImageCodeUtils {

	//	private static final String ALL = "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";

	private static final String ALL = "2345678wertyuipasdfghjkzxcvbnmQWERTYUIPASDFGHJKLZXCVBNM";//去除容易混淆的字母

	private static final Font[] fonts = getFonts();//本地字體庫

	private static final Random ran = new Random();//獲取隨機數

	/**
	 * 加載fonts文件夾下的所有字體
	 *
	 * @return
	 */
	private static Font[] getFonts() {
		File file = new File("fonts");
		File[] files = file.listFiles();
		Font[] fonts = new Font[files.length];
		for (int i = 0, len = files.length; i < len; i++) {
			try {
				fonts[i] = Font.createFont(Font.TRUETYPE_FONT, files[i]);
			} catch (FontFormatException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return fonts;
	}

	/**
	 * 獲取隨機顏色
	 *
	 * @return
	 */
	private static Color randomColor() {
		return new Color(ran.nextInt(256), ran.nextInt(256), ran.nextInt(256));
	}

	/**
	 * 獲取相似顏色
	 *
	 * @param color      樣本
	 * @param similarity 相似度(越小越相似)
	 * @return
	 */
	private static Color randomColor(Color color, int similarity) {
		int r = color.getRed() + ran.nextInt(similarity) - similarity / 2;
		int g = color.getGreen() + ran.nextInt(similarity) - similarity / 2;
		int b = color.getBlue() + ran.nextInt(similarity) - similarity / 2;
		if (r > 255) r = 255;
		if (r < 0) r = 0;
		if (g > 255) g = 255;
		if (g < 0) g = 0;
		if (b > 255) b = 255;
		if (b < 0) b = 0;
		return new Color(r, g, b);
	}

	/**
	 * 獲取隨機字符串
	 *
	 * @param len 驗證碼長度
	 * @return
	 */
	public static String randomCode(int len) {
		StringBuilder str = new StringBuilder();
		for (int i = 0; i < len; i++)
			str.append(ALL.charAt(ran.nextInt(ALL.length())));
		return str.toString();
	}

	/**
	 * 生成120*40字體大小爲40長度爲4的隨機驗證碼
	 *
	 * @return
	 */
	public static VerifyImageCode createVerifyCode() {
		return createVerifyCode(randomCode(4), 120, 40, 40);
	}

	/**
	 * 生成指定字符串驗證碼
	 *
	 * @param code     驗證碼
	 * @param width    圖片寬度
	 * @param height   圖片高度
	 * @param fontSize 字體大小
	 * @return
	 */
	public static VerifyImageCode createVerifyCode(final String code, int width, int height, int fontSize) {
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g = image.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);//抗鋸齒

		final float stroke = fontSize / 40f;//干擾線 噪點粗細

		Color backColor = randomColor();//背景顏色
		Color bodyColor = randomColor();//驗證碼顏色

		//先給背景上色
		g.setColor(backColor);
		g.fillRect(0, 0, width, height);

		//繪製干擾線
		g.setStroke(new BasicStroke(stroke));//干擾線粗細
		for (int i = 0; i < 20; i++) {
			g.setColor(randomColor());// 設置線條的顏色
			g.drawLine(ran.nextInt(width), ran.nextInt(height), ran.nextInt(width * 2) - width / 2, ran.nextInt(height * 2) - height / 2);
		}

		//繪製驗證碼
		Font font = fonts[ran.nextInt(fonts.length)].deriveFont(Font.BOLD, fontSize);//從我們的字體庫裏隨機取一種字體並重新設置樣式大小
		//Font.deriveFont(int style, float size)通過複製此 Font 對象並應用新樣式和大小,創建一個新 Font 對象
		g.setFont(font);
		FontDesignMetrics metrics = FontDesignMetrics.getMetrics(font);//通過這個類獲取字體的寬高 以保證驗證碼畫在圖片合適的位置
		int bodyX = width / code.length();//確定每個字符的位置
		for (int i = 0; i < code.length(); i++) {
			String str = code.substring(i, i + 1);//取第i個字符
			int strWidth = metrics.stringWidth(str);//字體寬度
			int strHeight = metrics.getHeight();//字體高度
			int asent = metrics.getAscent();//基線baseline之上的長度
			//xy爲每個字符的中心座標
			float x = i * bodyX + bodyX / 2f;
			float y = height / 2f;
			g.setColor(randomColor(bodyColor, 50));//獲取bodyColor相似顏色
			double angle = Math.random() * Math.PI / 2 - Math.PI / 4;//角度在-45°到45°之間
			g.rotate(angle, x, y);//旋轉畫筆
			g.drawString(str, x - strWidth / 2, y + asent - strHeight / 2);//把驗證碼的第i個字符畫到圖片上
			g.rotate(-angle, x, y);//再轉回來
		}

		//繪製干擾線
		for (int i = 0; i < 5; i++) {
			g.setColor(randomColor());// 設置線條的顏色
			g.drawLine(ran.nextInt(width), ran.nextInt(height), ran.nextInt(width * 2) - width, ran.nextInt(height * 2) - height);
		}

		//繪製噪點
		float ratio = 0.05f;//噪聲率
		//噪聲率×總面積÷點面積 保證圖片越大噪點不會越多
		int area = (int) (ratio * (width * height) / (stroke * stroke));
		for (int i = 0; i < area; i++) {
			g.setColor(randomColor());
			int x = ran.nextInt(width);
			int y = ran.nextInt(height);
			g.drawLine(x, y, x, y);
		}

		//生成id的規則你們自己改 這裏我就隨便寫了
		//例如 當前時間戳+Math.random()的md5值 或圖片的md5值保證id不會重複
		//主要是發送給前端驗證的時候要用到
		String id = "64aab58041013e68b479bd68a027515c";
		return new VerifyImageCode(id, code, image);
	}
}

4.創建類VerifyImageCode

相當於一個帶功能的實體類吧

package com.liziguo.utils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;

/**
 * @Author:Liziguo
 * @Date:2019/12/13 23:02
 */
public class VerifyImageCode {

	public final String id;

	public final String code;

	public final BufferedImage image;

	public VerifyImageCode(String id, String code, BufferedImage image) {
		this.id = id;
		this.code = code;
		this.image = image;
	}

	/**
	 * @return 獲取小寫驗證碼
	 */
	public String getLowerCode() {
		return code.toLowerCase();
	}

	/**
	 * @return 大寫驗證碼
	 */
	public String getUpperCode() {
		return code.toUpperCase();
	}

	/**
	 * 把圖片轉換成字節數組
	 *
	 * @return
	 */
	public byte[] toBytes() {
		ByteArrayOutputStream out = null;
		try {
			out = new ByteArrayOutputStream();
			ImageIO.write(image, "png", out);
			return out.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return null;
	}

	/**
	 * 生成圖片的base64編碼
	 *
	 * @return
	 */
	public String toBase64Code() {
		return Base64.getEncoder().encodeToString(toBytes());
	}

}

到了這一步直接調用VerifyImageCodeUtils.createVerifyCode就能生成驗證碼了不過還看不到效果
如果想看到效果請繼續往下走

5.創建測試類Test

package com.liziguo.utils;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileSystemView;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

/**
 * @Author:Liziguo
 * @Date:2019/12/14 0:30
 */
public class Test extends JFrame {

	private BufferedImage image;

	public Test(BufferedImage image) {
		this.image = image;
		setResizable(false);//用戶不能隨意調節窗口大小
		setSize(image.getWidth(), image.getHeight());//設置大小
		setLocationRelativeTo(null);//窗口居中
		setDefaultCloseOperation(3);//x掉後徹底退出程序
		setAlwaysOnTop(true);//窗口始終在最上方
		setUndecorated(true);//去除修飾(去除窗口邊框、最大化、最小化、關閉按鈕等)
		//添加一個鍵盤事件 按Esc關閉窗口 F5重新生成驗證碼
		addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.getKeyCode() == 27) {
					//27是鍵盤Esc的Ascii碼
					System.exit(0);
				} else if (e.getKeyCode() == 116) {
					//116是鍵盤F5的Ascii碼
					reImage();
				}
			}
		});
		setVisible(true);//顯示
	}

	private void reImage() {
		String code = VerifyImageCodeUtils.randomCode(4);//隨機生成4位數字符串
		VerifyImageCode v = VerifyImageCodeUtils.createVerifyCode(code, 1200, 400, 400);//生成圖形驗證碼
		image = v.image;
		setSize(image.getWidth(), image.getHeight());//設置大小
		setLocationRelativeTo(null);//窗口居中
		repaint();//重繪
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		//這裏我們重寫一下paint方法 把圖片畫出來
		g.drawImage(image, 0, 0, image.getWidth(), image.getHeight(), null);
	}

	public static void main(String[] args) throws IOException {
//		String code = "龘亣龖䪏";
		String code = VerifyImageCodeUtils.randomCode(4);//隨機生成4位數字符串
		//最佳寬高計算公式 height=fontSize  width=height*len*0.75
		VerifyImageCode v = VerifyImageCodeUtils.createVerifyCode(code, 1200, 400, 400);//生成圖形驗證碼

		new Test(v.image);//圖形界面測試驗證碼

		//獲取桌面路徑
		File file = new File(FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath() + "/verifyImage.png");
		//把這張驗證碼保存到桌面
		ImageIO.write(v.image, "png", file);

		System.out.println("驗證碼信息:");
		System.out.println("id:" + v.id);
		System.out.println("code:" + v.code);
		System.out.println("lowerCode:" + v.getLowerCode());
		System.out.println("upperCode:" + v.getUpperCode());
		System.out.println("imageBase64Code:" + v.toBase64Code());
	}
}

最終目錄結構
在這裏插入圖片描述

7.運行效果

在這裏插入圖片描述
在這裏插入圖片描述

8.補充

有沒有發現最後生成了一串長的base64編碼
把這一串編碼放到img標籤在前面加上"data:image/gif;base64,"是能直接使用的

<img src="data:image/gif;base64,(你的base64編碼)"/>

驗證碼最佳寬高計算公式:字體大小和圖片高度相等,圖片寬度=圖片高度×驗證碼長度×0.75

由於這裏使用的不是本地字體 所以直接丟到linux系統也能正常運行

驗證碼可以輸入中文 前提是你的字體支持中文

下載地址:不要忘記點個贊哦

最後送大家一張1920×1080 字體大小400的驗證碼壁紙 哪去用吧

i miss you

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章