本文的来由主要是满足自己的好奇心,而不是证明什么东西,如果涉及到什么官方性的事情,麻烦通知我谢谢;本篇将要和大家分享的是一个看起来通12306图片验证码相似的效果,这篇应该是今年农历最后一篇分享文章了,楼主明天就要坐火车回家了,预祝各位:新年快乐,明年事事顺利,如果可以给我发个红包吧呵呵,希望大家能够喜欢,也希望各位多多"扫码支持"和"推荐"谢谢;
» 效果图展示及分析
» C#合并多张图片和获取图片验证码粗略的算法
» MVC如何使用图片验证码
» 2016年一句总结
» 2017年一句展望
下面一步一个脚印的来分享:
» 效果图展示及分析
首先,咋们来看一个图片验证码效果地址::1001/home/ValidCode,及效果图:
上图是最终的示例图样,选中图片截图的网上的(如侵权,请及时联系作者);这里看起来类似于火车票网站的验证码,咋们先来分析下这种验证码是怎么把用户选中的信息传递个后端的吧,直接用12306官网为例,打开该网址并查看验证码,锁定验证码对应的html元素,然后尝试这点击某个图片,能看到如图增加的html节点:
、然后,咋们再看“登录”按钮对应的js文件https://kyfw.12306.cn/otn/resources/merged/login_js.js,通过查找对应的登录按钮id=“loginSub”,然后能够找到其对应有一个验证码验证的操作:
很明显这里是通过id=“randCode”传递进来选中的验证码参数,然后我们再通过此id去查找html页面中对应的html元素是:
通过这里对应元素里面的value="106,115,182,113,252,47"和我们选中按钮是生成的div对应的left,top的值对比能发现数量上和值都很接近,并且left对比都相差3,top值对比相差16,再加上登录按钮js事件中用到了其对应的id=“randCode”,由此我们可以大胆猜测这个隐藏文本randCode对应的值就是传递给后端对比验证码是否匹配的值,这里较以往验证码不同的是,是使用坐标来确定选中验证图片是否匹配,由此引发了咋们对后台对比验证码是否正确的猜想,后台应该是每张单元格小图片都会对应一组起始,终止的坐标,就只能是这样才能判断出用户选中的图片坐标是否包含在此单元格小图片允许范围内,这种猜想的流程是否符合逻辑还请各位多多指正;有了上面的猜想,下面我们就可以来实现具体的代码了,鉴于篇幅影响下面只给出重要的几个方法;
» C#合并多张图片和获取图片验证码粗略的算法
看到12306的图片验证码图片,每张上面都有很多小图片组成,因此有了两种猜想:1.真的是由工作人员处理后把所有小图片弄成一个大的静态真实图片;2.通过程序由多张小图片合并成一个大图片流;不难看出前者如果处理起来需要耗费大量的工作周期(当然火车票那么来钱,说不定就是这么干的呢,谁知道呢),反正我是选择了后者通过程序处理合并多张图片,因此有了以下代码:
生成验证码图片流 [] CreateImgCodeStream(ref List<MoImgCode> imgCode, int width = 283, int height = 181) 9 { [0]; padding = 1; 13 var lenNum = 2; 14 var len = imgCode.Count; 15 var len_len = len / lenNum; 16 var image = new Bitmap(width, height); 17 var g = Graphics.FromImage(image); { 20 var random = new Random(); g.Clear(Color.White); 23 var ii = 1; 24 var everyX = width / len_len; 25 var everyY = height / lenNum; 26 foreach (var item in imgCode) 27 { 28 var img = Image.FromFile(item.ImgUrl); x2 = everyX * (ii > len_len ? ii - len_len : ii); 31 var y2 = everyY * (ii > len_len ? 2 : 1) + (ii > len_len ? padding : 0); (ii == len_len) 34 { 35 g.DrawLine(new Pen(Color.Silver), 0, everyY, width, everyY); 36 } x1 = x2 - everyX + padding; 39 var y1 = y2 - everyY; 40 41 g.DrawImage(img, x1, y1, everyX, everyY); (item.IsChoice) 45 { 46 item.Point_A = new Point() 47 { 48 X = x1, 49 Y = y1 50 }; 51 item.Point_B = new Point 52 { 53 X = x1 + everyX, 54 Y = y1 + everyY 55 }; 56 } 57 58 ii++; 59 } (int i = 0; i < 100; i++) 62 { 63 var x = random.Next(image.Width); 64 var y = random.Next(image.Height); 65 image.SetPixel(x, y, Color.FromArgb(random.Next())); 66 } g.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1); stream = new MemoryStream(); 72 image.Save(stream, ImageFormat.Jpeg); bb = stream.ToArray(); 75 } 76 catch (Exception ex) { } { 79 g.Dispose(); 80 image.Dispose(); 81 } 82 return bb; 83 }
通过传递小图片集合,然后内部通过画布把小图片画到同一个大图片中去,并且返回其对应在大图片所在的坐标(前面咋们提到的起始坐标,终止坐标):
由图能看出每张小图片都有自己相对于大图片原点的坐标,这也是咋们判断用户选择的图片点是否在每个小图片坐标范围内的依据,因此需要通过画图片的时候获取出来;
再来,咋们有了画图片的方法还不够,还需要有一个获取随机小图片的方法,我这里代码简单并非是最好的获取随机小图片方法仅供参考,先上获取程序文件夹下面图片的方法: