zoukankan      html  css  js  c++  java
  • java生成简单验证码图片

    概要

      最近项目需要用java实现输出随机验证码图片到前台,正好有机会接触下java的绘图类,完成需求后也有时间做个总结,写篇随笔记录下也希望能帮助到有同样需求的人!

    需求流程图

    1.生成随机数

      在java中生成随机数无非就是调用Random的api,但为了后续更好的实用,应该分成多种组合以适应需求的变化,应将生成随机数的个数和类型组合还有排除字符抽取成参数,这个也比较简单,没有什么难度,就直接贴上代码

     1 /**
     2      * 生成随机验证码
     3      * @param type  类型
     4      * @param length   长度
     5      * @param exChars  排除的字符
     6      * @return
     7      */
     8     public static String getRandomCode(int type,int length,String exChars){
     9       case TYPE_NUM_CHAR://数字+(大小写)字母
    10             while(i<length){
    11                 int t=random.nextInt(123);
    12                 if((t>=97||(t>=65&&t<=90)||(t>=48&&t<=57))&&(exChars==null||exChars.indexOf((char)t)<0)){
    13                     sb.append((char)t);
    14                     i++;
    15                 }
    16             }
    17             break;
    18     
    19

    2.绘制图片

      绘制图片是重点也是难点,老实说我之前都没用过BufferedImage、Graphics、Color等对象,都是从度娘处脑补并结合帖子写出来的输出图片

      1)创建BufferedImage对象

        如果把BufferedImage比作是画板,那么Graphics就是画纸,这样比较好理解,后续的操作都是在这画纸上进行的

    1     BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
    2     Graphics g = image.getGraphics();
    3     //随机操作对象
    4     Random r=new Random();

      2)绘制背景

       Graphics有个setColor()方法,可理解为画笔,在绘制任何东西前要选好画笔,即颜色,然后调用fillRect()进行轮廓的绘制,后续的绘制范围不会超过这个轮廓

    1         g.setColor(backColor==null?getRandomColor():backColor);
    2         g.fillRect(0,0,width,height);

      3)绘制干扰线

      调用drawLine画直线,绘制不超过interLine条干扰线

     1       if(interLine>0){
     3             int x=r.nextInt(4),y=0;
     4             int x1=width-r.nextInt(4),y1=0;
     5             for(int i=0;i<interLine;i++){
     6                 g.setColor(lineColor==null?getRandomColor():lineColor);
     7                 y=r.nextInt(height-r.nextInt(4));
     8                 y1=r.nextInt(height-r.nextInt(4));
     9                 g.drawLine(x,y,x1,y1);
    10             }
    11         }

       4)写验证码

        写验证码时调用drawString()方法,为了不整整齐齐而且重叠的写,应将每个字符的高度和水平位置随机,重点在于每绘制完一个字符后,需将画笔的水平坐标往右边移动一定的位置,这里我用了依据宽度浮动,可不必照搬这里的,有合适的算法就行

     1      int fsize=(int)(height*0.8);//字体大小为图片高度的80%
     2         int fx=0;
     3         int fy=fsize;
     4         g.setFont(new Font(Font.SANS_SERIF,Font.PLAIN,fsize));
     5         //写字符
     6         for(int i=0;i<textCode.length();i++){
     7             fy=randomLocation?(int)((Math.random()*0.3+0.6)*height):fy;//每个字符高低是否随机
     8             g.setColor(foreColor==null?getRandomColor():foreColor);
     9             g.drawString(textCode.charAt(i)+"",fx,fy);
    10             fx+=(width / textCode.length()) * (Math.random() * 0.3 + 0.8); //依据宽度浮动
    11        }

      5)扭曲图片

        图片的扭曲就是将图片水平和垂直按不同比例平移,copyArea()方法简单明了,直接是复制区域,也是同个意识

     1      //扭曲图片
     2          shearX(g, width, height, backColor);
     3          shearY(g, width, height, backColor);    
     4          private static void shearX(Graphics g, int w1, int h1, Color color) {
     5          Random random=new Random();
     6          int period = 2;
     7   
     8          boolean borderGap = true;
     9          int frames = 1;
    10         int phase = random.nextInt(2);
    11  
    12         for (int i = 0; i < h1; i++) {
    13             double d = (double) (period >> 1)* Math.sin((double) i / (double) period 
    14           + (2.2831853071795862D * (double) phase)/ (double) frames);
    15             g.copyArea(0, i, w1, 1, (int) d, 0);
    16             if (borderGap) {
    17                 g.setColor(color);
    18                 g.drawLine((int) d, i, 0, i);
    19                 g.drawLine((int) d + w1, i, w1, i);
    20             }
    21         }
    22  
    23     }
    24  
    25     private static void shearY(Graphics g, int w1, int h1, Color color) {
    26         Random random=new Random();
    27         int period = random.nextInt(40) + 10; // 50;
    28  
    29         boolean borderGap = true;
    30         int frames = 20;
    31         int phase = random.nextInt(2);
    32         for (int i = 0; i < w1; i++) {
    33             double d = (double) (period >> 1)
    34                     * Math.sin((double) i / (double) period
    35                             + (2.2831853071795862D * (double) phase)/ (double) frames);
    36             g.copyArea(i, 0, 1, h1, 0, (int) d);
    37             if (borderGap) {
    38                 g.setColor(color);
    39                 g.drawLine(i, (int) d, i, 0);
    40                 g.drawLine(i, (int) d + h1, i, h1);
    41             }
    42  
    43         }
    44  
    45     }

       6)添加噪点

        噪点的添加实质上就是在画纸上没有规律的点点点...,所以用随机颜色随机位置来执行image.setRGB()方法最合适了

    1      float yawpRate = 0.05f;// 噪声率
    2         int area = (int) (yawpRate * width * height);//噪点数量
    3         for (int i = 0; i < area; i++) {
    4             int xxx = r.nextInt(width);
    5             int yyy = r.nextInt(height);
    6             int rgb = getRandomColor().getRGB();
    7             image.setRGB(xxx, yyy, rgb);
    8         }

      7)封笔

      以上一切画完之后,下一步就是封笔了,思来想去这样表达最合适,绘制完之后就是放好笔,收起画纸,这里就是返回BufferedImage对象

    1         g.dispose();
    2         return image;    

     输出目标位置

      1)输出文件

      这里写了一个简单的测试类,将图片输出成文件

     1 public static void main(String[] args) {
     2         String randomCode = CodeUtils.getRandomCode(CodeUtils.TYPE_NUM_CHAR, 4, null);
     3         System.out.println(randomCode);
     4         BufferedImage imageFromCode = ImageUtils.getImageFromCode(randomCode, 100, 50, 3, true, Color.WHITE, Color.BLACK, null);
     5          try {
     6             File file = new File("d:/test01.jpg");
     7             ImageIO.write(imageFromCode,"jpg",file);
     8             System.out.println("成功保存到:"+file.getAbsolutePath());
     9         } catch (IOException e) {
    10             System.out.println("保存失败");
    11             e.printStackTrace();
    12         } 
    13         
    14     }

        效果图:

        

      2)输出流

        输出到流中也是用到和到文件中的方法,根据业务保存到目标位置即可

     1     public static void outputStream(HttpServletRequest request,HttpServletResponse response){
     2         try {
     3             // 设置浏览器不缓存本页
     4             response.addHeader("Pragma", "no-cache");
     5             response.addHeader("Cache-Control", "no-cache");
     6             response.addHeader("Expires", "0");
     7             // 生成验证码,写入用户session
     8             String verifyCode = CodeUtils.getRandomCode(CodeUtils.TYPE_NUM_CHAR, 4, null);
     9             request.getSession().setAttribute("imageVerify", verifyCode);
    10             // 输出验证码给客户端
    11             response.setContentType("image/jpeg");
    12             BufferedImage bim = ImageUtils
    13                     .getImageFromCode(verifyCode, 47, 18, 3, true,
    14                             Color.WHITE, Color.BLACK, null);
    15             ImageIO.write(bim, "JPEG", response.getOutputStream());
    16 
    17         } catch (Exception e) {
    18         }
    19         return null;
    20     }

    总结

      也没有比较深入的去分析绘制类,有需求可以通过调整位置参数来实现自己要的效果

  • 相关阅读:
    生成26个字母
    讲讲promise
    leetcode每日一题(2020-07-27)392. 判断子序列
    leetcode每日一题(2020-07-21):95. 不同的二叉搜索树 II
    leetcode每日一题(2020-07-18):97. 交错字符串
    【Python基础知识】(九)文件和异常
    【Python基础知识】(八)类和继承
    es6 语法
    ant-design-vue 之form表单中label-col和wrapper-col使用
    ant-design-vue 之form表单使用
  • 原文地址:https://www.cnblogs.com/maixiaodou/p/7305729.html
Copyright © 2011-2022 走看看