用java生成gif格式的验证码
图片水印admin 发布于:2013-10-31 10:18:00
阅读:loading
jsp验证码网上一搜一大堆,早些时候我收藏的一个(就是那个项目中最常见的那种)验证码(给个图一看便知),一个servlet搞定,但昨天无意中在网上看到gif的验证码的一篇文章,于是就找了找java生成gif图片相关的资源,发现真的出奇的少,后来终于找到了一个博文简短几行代码就生成gif图片的,评价还是很不错的,于是把这些代码拷贝出来之后发现编译不通过,是少了个AnimatedGifEncoder类,快捷键提示也提示不出来,心里想着,什么瘠薄例子撒,代码各种不全,也不做个代码运行环境的介绍,呵呵,网络上就是这样。接着就是google了一下这个类,找到了它的源码,虽然不是很懂的样子但还是拷贝了下来存储在本地工程当中,结果又发现编译不通过,又有两处报红XX的地方,仔细查看才知又少了几个类。。。。。。此处省略吧,换个话题。
在这里通过这个实践跟大家分享两个经验吧。
一、eclipse强大的Shift + Alt + A 快捷键功能。
此快捷键的用途
我们可谓是常常从网上拷贝代码到eclipse当中,大多数的博客或日志中的代码块都是有行号的,直接选择代码复制出来的代码肯定是不能使用的,必须去掉开头的行号和.才行,记得最开始不知道此快捷键的时候鄙人是一行一行删除的,后来也是从一个同事那儿知道的,慢慢慢慢的广泛的使用起来(鄙人自己广泛使用)、、、、解释下此快捷键的用途吧,按下此快捷键之后,可以像截图一样选择无规律的一片代码,然后可以对这块代码进行删除、复制等等。见下图:
二、如何快速的找到不熟悉的类文件所在地以及其源码。
快速查找不熟悉的文件的源代码
解决这个问题,我推荐这个网站 http://www.docjar.com/ ; 专门查找jar文件的,当然也能找到源代码和javadoc,比如AnimatedGifEncoder.java文件中还需要找到NeuQuant.java和LZWEncoder.java,我都是在这里找到的。
=========分割一样,以下重点介绍下gif的验证码=========
允许我把这几个文件归以下类吧,将他们称之为一个组件吧(网上也有人说是借助于洋人写的类),先根据一个最基础的例子来熟悉以下生成gif的代码吧。
三、查看简单的代码示例燃起对这个知识点的兴趣吧
TestCreateGif.java(不要纠结于我的类命名,需要用到的AnimatedGifEncoder文件需注意源代码下载)
package com.gif;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
public class TestCreateGif {
final static String path = "f:\\tt\\gifdemo\\";
public static void create() throws Exception {
BufferedImage fileA = ImageIO.read(new File(path+ "a.jpg"));
BufferedImage fileB = ImageIO.read(new File(path+ "b.jpg"));
BufferedImage fileC = ImageIO.read(new File(path+ "c.jpg"));
AnimatedGifEncoder gif = new AnimatedGifEncoder();
gif.setRepeat(0);//设置是否重复切换
gif.start("f:\\tt\\gifdemo\\demo.gif");
gif.setDelay(500);//设置每次切换时的时间间隔,单位毫秒
gif.addFrame(fileA);//添加一帧的图片
gif.setDelay(500);
gif.addFrame(fileB);
gif.setDelay(500);
gif.addFrame(fileC);
boolean result = gif.finish();
System.out.println(result);
}
public static void main(String[] args) throws Exception {
create();
}
}
代码就这么简单易懂,看下运行结果吧。
以上代码中的a.jpg,b.jpg,c.jpg原图为:
生成的demo.gif原为:
四、改造最开始提到的那种最简单的验证码吧,将之改造成gif动态的
有了上面的小例子,相信改造下原来的版本应该不难吧, 直接给出改造后的代码吧。
package com.gif;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Arrays;
import java.util.Random;
/**
* 暂时就命名为这个吧
* @author chendd
*
*/
public class TestCreateGif2Old {
/**
* 生成随机数的源,排除数字4
*/
final static String RANDOM_CHAR = "abcdefijklmnopqrstuvwxyzABCDEFIJKLMNOPQRSTUVWXYZ012356789";
/**
* 生成的验证码位数的长度,一般生成的是 4 位字母数字的随机数
*/
final static int CODE_LENS = 4;
/**
* 设置生成的gif图片轮流切换的图片张数
*/
final static int CHANGE_COUNT = 3;
/**
* @param args
* @throws Exception
*/
public static void main(String args[]) throws Exception {
long start = System.currentTimeMillis();
create("f:\\tt\\gifdemo\\abcdefg.gif");
long end = System.currentTimeMillis();
System.out.println("所用时间为:" + (end - start) + "ms");
}
/**
* 创建生成函数
* @throws Exception
*/
public static void create(String filePath) throws Exception {
//按位数计算出N位长度的验证码图片所占用的宽和高,这里如果生成固定位数的长度时可以将宽和高写成常量值
int width = CODE_LENS * 15 - CODE_LENS * 15 / 10 + 6, height = 20;
// 在内存中创建图象
AnimatedGifEncoder gifFrame = new AnimatedGifEncoder();
BufferedImage frame;
//这里需注意下,如果在servlet中输出图片至网页则需要使用gif.start(fos);函数
gifFrame.start(filePath);//开始写入文件
gifFrame.setRepeat(0);//0重复,1不重复
gifFrame.setDelay(600);//设置图片直接的延时600毫秒
//生成 N 位长度的随机数,做为验证码
char[] randCodes = new char[CODE_LENS];
for(int i = 0 ; i < CODE_LENS ; i ++){
randCodes[i] = getRandomChar();
}
for(int i = 0 ; i < CHANGE_COUNT ; i ++){
frame = graphicsImage(randCodes , width, height);
gifFrame.addFrame(frame);
}
gifFrame.finish();
System.out.println("生成的code为:" + Arrays.toString(randCodes));
// 如果是在servlet中使用则输出图象到页面,同时将code存储值session范围内,省略......
}
/**
* 在内存中生成一个图片
* @param randCodes
* @param width
* @param height
* @return
*/
private static BufferedImage graphicsImage(char[] randCodes , int width, int height) {
Random random = new Random();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
Graphics g = image.getGraphics();
// 设定背景色
g.setColor(getRandColor(200, 250));
g.fillRect(0, 0, width, height);
// 设定字体
g.setFont(new Font("Times New Roman", Font.CENTER_BASELINE, 18));
// 画边框
// g.setColor(new Color());
// g.drawRect(0,0,width-1,height-1);
// 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
g.setColor(getRandColor(160, 200));
for (int i = 0; i < 40; i++) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x, y, x + xl, y + yl);
}
// 取随机产生的认证码(4位数字)
for (int i = 0; i < randCodes.length; i++) {
// 将认证码显示到图象中
g.setColor(new Color(20 + random.nextInt(110), 20 + random
.nextInt(110), 20 + random.nextInt(110)));
// 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.drawString(String.valueOf(randCodes[i]), 13 * i + 6, 16);
}
// 图象生效
g.dispose();
return image;
}
/**
* 给定范围获得随机颜色
* @param fc
* @param bc
* @return
*/
private static Color getRandColor(int fc, int bc) {
Random random = new Random();
if (fc > 255)
fc = 255;
if (bc > 255)
bc = 255;
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);
}
/**
* 获取一定长度的随机数
* @return
*/
private static Character getRandomChar() {
int lens = RANDOM_CHAR.length();
int randomInt = new Random().nextInt(lens);
Character c = RANDOM_CHAR.charAt(randomInt);
return c;
}
}
控制台输出结果如下:
生成的code为:[N, N, 3, 3]
所用时间为:566ms
生成的图片文件如下:
调用了好几次,竟然能生成出两个都一样的验证码,那就用它了,生成的图为:
五、源码下载
源码下载还是沿承了内涵图片的方式,将上图中的NN33所在的图片存储至本地,然后使用压缩包工具打开,即可看到src-code.zip文件。有什么问题也可以直接点击下图的联系我。
@2013-12-18添加
最近整改的一个项目中,其也使用到了gif的验证码,效果如下:此图生成的代码在保存图片时被我修改我,我将延迟时间调大了一些,源码里面是100毫秒闪烁的),这种效果在看上去在安全性上更好一些的样子,但此处的效果貌似太闪了一些,个人觉得主要就是,闪烁效果在每次变化的时候,上面的字符基本都是3位数呈现的,如果用上面的代码来实现这种效果的话,相当于生成4帧的图片,每个图片中都显示当前字符长度的字符,即循环1次时生成1个字符的图片,循环第2次时生成2个字符的图片,一次类推(这里说下修改的原理,并为新手试验),导致用户在录入验证码时一眼看上去看不完全,至少我是得看4眼,眼在每个字符上停留一眼,谈谈意见而已,不喜勿怒。
另外我上面的代码中排除了感觉不好的数字4,发现其里面排除了O和I这些,这些都是容易混淆的字符,挺好的。
@2013-12-19添加
Java学习室中也有这个源码,http://www.java3z.com/cwbwebhome/article/article3/3915.html?id=4895
点赞