[理论知识]
在java程序开发中,要显示一张图片,主要有两种方式。第一种是基于现成图片,这种方式比较常见也比较简单。第二种是使用java中java.awt包和com.sun.image包中的绘图技术,使用代码绘制出一张图片,这种技术一般适用于图片并非固定、重复使用率较低的场景,比如验证码、图片加水印、生成缩略图、绘制各种数据分析图(柱状图、饼图、线性图等),这些图片并不是固定的,而是根据数据的改变而随时变化的。
[步骤解读一]绘制验证码
本文中,小博老师就为大家解析一下,如何使用java绘图技术来制作验证码,由于验证码的功能比较常用,代码又比较繁琐,因此小博老师先为大家封装一个Identifying类,以后要使用验证码就可以直接调用它了,核心代码如下:
package com.bwf.framework.javaee.imageutils;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
/**
* 验证码图片生成Servlet类,直接调用该Servlet即可使用 取值的时候调用session.getAttribute("code")得到生成的值
* @author 博为峰-小博老师
* @time 2016-11-1 下午14:26:00
*/
public class Identifying extends HttpServlet {
public Identifying(HttpServletRequest request, HttpServletResponse response){
this.setRequest(request);
this.setResponse(response);
}
private static final long serialVersionUID = 1L; // 对象序列号
private int width = 120; // 画布宽度(px)
private int height = 30; // 画布高度(px)
private int lineNum = 150; // 干扰线数量
private int pointNum = 100; // 干扰点数量
private int wordNum = 4; // 验证码字符个数
private HttpServletRequest request; // 当前请求对象
private HttpServletResponse response; // 当前响应对象
private String sessionName = "code"; // 正确验证码存放在Session对象中的key名称
/**
* 获取范围内的随机颜色
* @param fc 颜色范围的最大值(0~255)
* @param bc 颜色范围的最小值(0~255)
* @return 随机颜色值
*/
private Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255)
fc = 255;
if (fc < 0)
fc = 0;
if (bc > 255)
bc = 255;
if (bc < 0)
bc = 0;
int r = fc + random.nextInt(bc - fc);
int g = fc + random.nextInt(bc - fc);
int b = fc + random.nextInt(bc - fc);
return new Color(r, g, b);
}
/**
* 绘制验证码图片
*/
public void create() throws IOException, ServletException {
// 设置输出类型为图片
response.setContentType("image/jpeg");
// 获取画布宽度和高度
int width = this.width;
int height = this.height;
// 产生随机数
Random r = new Random();
// 产生缓冲画布
BufferedImage imgbuf = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 取得缓冲的绘制环境
Graphics2D g = imgbuf.createGraphics();
// 设置笔触颜色 用来填充画布的颜色
g.setColor(getRandColor(200, 250));
// 填充画布
g.fillRect(0, 0, width, height);
// 设计笔触颜色 用来绘制干扰线
g.setColor(getRandColor(160, 200));
// 随机产生lineNum条干扰线,使图象中的认证码不易被其它程序探测到
for (int i = 0; i < this.lineNum; i++) {
int x = r.nextInt(width); // 干扰线起始点x轴坐标
int y = r.nextInt(height); // 干扰线起始点y轴坐标
int xl = r.nextInt(12); // 干扰线结束点x轴坐标
int yl = r.nextInt(12); // 干扰线结束点y轴坐标
g.drawLine(x, y, x + xl, y + yl); // 绘制干扰线
}
// 设置笔触颜色 用来绘制干扰点
g.setColor(getRandColor(120, 240));
// 随机产生pointNum个干扰点,使图像中的验证码不易被其他分析程序探测到
for (int i = 0; i < this.pointNum; i++) {
int x = r.nextInt(width); // 设置干扰点中心点x轴坐标
int y = r.nextInt(height); // 设置干扰点中心点y轴坐标
g.drawOval(x, y, 0, 0); // 绘制干扰点,半径0,起始角度0
}
// 设置文字样式 用来绘制验证码字符
g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
// 准备一个ArrayList 用于存放验证码字符库
ArrayList libs = new ArrayList();
// 准备一个char[] 列出验证码字符库
char[] arr = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q',
'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6','7', '8', '9' };
// 将char[]中的验证码字符 存放到 ArrayList中
for (int i = 0; i <= arr.length - 1; i++) {
libs.add(arr[i] + "");
}
// 随机打乱顺序
Collections.shuffle(libs);
// 定义字符串 用于拼接正确验证码
String scode = "";
// 循环出wordNum个验证码字符
for (int i = 0; i < this.wordNum; i++) {
// 从随机验证码字符库 抽取一个验证码字符
String rand = String.valueOf(libs.get(i));
// 拼接正确验证码
scode += rand;
// 设置笔触颜色 用于绘制验证码字符
g.setColor(new Color(20 + r.nextInt(110), 20 + r.nextInt(110), 20 + r.nextInt(110)));
// 绘制验证码字符
g.drawString(rand, (this.width / this.wordNum / 2 + 12) * i + (this.width / this.wordNum / 2),this.height / 2 + 6);
}
// 将正确验证码存放到Session对象中
request.getSession().setAttribute(this.sessionName, scode);
// 得到HTTP的流
ServletOutputStream out = response.getOutputStream();
// 产生JPEG的图像加码器
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
// 将图像画布内容转码
encoder.encode(imgbuf);
// 输出图像
out.flush();
}
……省略getter和setter……
}
然后我们在一个Servlet,实例化Identifying对象,调用create()方法,即可显示出一个验证码图片,核心代码如下:
@WebServlet("/BWFIdentifying")
public class BWFIdentifyingServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Identifying iden = new Identifying(request, response);
iden.create();
}
}
浏览器访问效果如下:
[步骤解读二]在登录页面中引入验证码
接下来我们制作一个简单的登录页面,引入验证码,我们创建一个jsp,核心代码如下:
<body>
<form action="BWFCheckLogin" method="post">
账户名称:<input type="text" name="username"/><br/><br/>
账户密码:<input type="password" name="password"/><br/><br/>
验证 码:<input type="text" name="iden" style="width:60px;"/>
<img src="BWFIdentifying" title="看不清?点击换一张!" style="cursor:pointer;" id="iden" /><br/><br/>
<input type="submit" value="登 录"/>
</form>
</body>
访问jsp页面效果如下:
为了实现验证码图片点击后可以自动更换一张验证码的效果,我们在该jsp页面中增加javascript,核心代码如下:
<script>
window.onload=function(){
document.getElementById("iden").onclick=function(){
this.src="BWFIdentifying?a="+Math.random();
}
}
</script>
其中在URL中传递参数a,使用随机数作为值,是为了让该Servlet认为每次都是新的请求。
这样,我们的验证码效果基本就做完了,当用户点击登录按钮,提交页面后,我们只需要获取表单提交的用户填写的验证码,然后从Session对象(默认key为“Code”)获取正确的验证码,进行比对即可。
[步骤解读三]Identifying类的常用方法
小博老师为大家封装的Identifying类,除了上述默认使用方式,还可以按照自己的需求,设置常用参数,例如:
@WebServlet("/BWFIdentifying")
public class BWFIdentifyingServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Identifying iden = new Identifying(request, response);
iden.setWidth(300); // 设置验证码画布宽度
iden.setHeight(50); // 设置验证码画布高度
iden.setLineNum(30); // 设置干扰线数量
iden.setPointNum(50); // 设置干扰点属相
iden.setWordNum(6); // 设置验证码字符数量
// 设置正确验证码存放Session对象的key名称
iden.setSessionName("iden"); i
den.create();
}
}
访问后效果:
来源:oschina
链接:https://my.oschina.net/u/2971691/blog/779597