.Net点选验证码实现思路分享

哈哈好久没冒泡了,最进看见点选验证码有点意思,所以想自己写一个。
先上效果图
2019-05-22-14-55-15
如果你被这个效果吸引了就请继续看下去。

贴代码前先说点思路:

1.要有一个汉字库,并按字形分类。(我在数据库里是安部首分类的)

2.获取验证码(也就是取几个文字做验证码)

3.根据取出来的文字去找形近字

4.排列验证码文字和形近字

5.绘制图片

6.显示

6.写个博客分享一下(分享代码改变世界)

获取字库

  我国文化博大精深,辣么多的字从哪儿来?当然我不可能手动加进去,于是我就在网上随便找了一个能查汉字的网站,去抓别人的数据。抓数据的方法请点传送门。传送门里说的只是思路,如果有不明白的请艾特我。我会在下面共享我的字库。

获取验证码

这个就比较简单了这里我就直接贴代码了,下面的代码就是随机排序后取4条数据,我这样写是为了图方便。个人觉得先随机生成ID,然后直接根据ID取数据,这样查询速度会比下面这种写法快。(注意我用的数据库是MySql)

1
2
3
4
5
6
7
8
9
10
11
/// <summary>
/// 获取验证码
/// </summary>
public List<VerificationCode.Model.WenZhi> GetCodeText()
{
const string sql = "SELECT * FROM wenzhi ORDER BY RAND() LIMIT 4";
var dataReader = dbHelper.GetDataReader(sql);
var list = DataReaderToList(dataReader);
dataReader.Close();
return list;
}

根据取出来的文字去找形近字

  因为第一步的时候我存部首了,所以这里我直接根据部首取获取当前部首的形近字。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// <summary>
/// 获取答案备选
/// </summary>
/// <param name="buShouCode">部首编码</param>
/// <param name="id">当前文字ID</param>
/// <param name="number">数量</param>
/// <returns></returns>
public List<VerificationCode.Model.WenZhi> GetAnswer(string buShouCode, int id,int number=1)
{
string sql = $"SELECT * FROM wenzhi where BuShouCode='{buShouCode}' and ID <> {id} ORDER BY RAND() LIMIT "+ number;
var dataReader = dbHelper.GetDataReader(sql);
var list = DataReaderToList(dataReader);
dataReader.Close();
return list;
}

排列验证码文字和形近字

  下面的代码是先把备选答案和验证码放在一个集合里然后再对集合排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public Model.Code GetCode()
{

var wenzlist = _wenZhiDal.GetCodeText(); //获取验证码
var listAnsuwr = new List<Answer>();//实例化备选答案对象
var answerCode = string.Empty;//答案
var result = new Model.Code
{
Id = Guid.NewGuid().ToString()
};
//根据验证码获取备选答案并把添加到答案添加到备选答案集合
foreach (var item in wenzlist)
{
answerCode += item.ID + ",";
result.AnswerValue += item.Text;
var answerList = _wenZhiDal.GetAnswer(item.BuShouCode, item.ID);
listAnsuwr.Add(new Answer { Id = item.ID.ToString(), Img = GetImage(item.Text) });
listAnsuwr.AddRange(answerList.Select(answer => new Answer { Id = answer.ID.ToString(), Img = GetImage(answer.Text) }));
}
//如果答案个数不够就再去取几个
if (listAnsuwr.Count < 9)
{
var ran = new Random();
var randKey = ran.Next(0, 4);
var item = wenzlist[randKey];
var answerList = _wenZhiDal.GetAnswer(item.BuShouCode, item.ID, 9 - listAnsuwr.Count);
listAnsuwr.AddRange(answerList.Select(answer => new Answer { Id = answer.ID.ToString(), Img = GetImage(answer.Text) }));
}
result.CodeImg = GetImage(result.AnswerValue);//获取图片
result.AnswerValue = answerCode.TrimEnd(',');
result.Answer = RandomSortList(listAnsuwr);//打乱正确答案与形近字的顺序
return result;
}

这是对集合排序的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// <summary>
/// 随机排列集合
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="listT"></param>
/// <returns></returns>
private static List<T> RandomSortList<T>(IEnumerable<T> listT)
{
var random = new Random();
var newList = new List<T>();
foreach (var item in listT)
{
newList.Insert(random.Next(newList.Count + 1), item);
}
return newList;
}

绘制图片

下面是画图的代码,验证码和备选答案对应两种不同的画法(里面注释写的还算清楚)。不要担心文字旋转x°后人类分不出来,哈哈。代码最后一句我把图片转成了Base64,方便前端调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private static string GetImage(string text)
{
Image image;
switch (text.Length)
{
case 1:
image = new Bitmap(50, 40);
break;
case 4:
image = new Bitmap(120, 40);
break;
default:
image = new Bitmap(50, 40);
break;
}
Brush brushText = new SolidBrush(Color.FromArgb(255, 0, 0, 0));
var graphics = Graphics.FromImage(image);
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.Clear(Color.White);
var font = new Font(new FontFamily("华文彩云"), 20, FontStyle.Regular);
if (text.Length > 1)//画验证码
{
//先来两条直线做干扰 然后再画文字
graphics.DrawLine(new Pen(brushText, new Random().Next(1, 3)), new Point(new Random().Next(0, 10), new Random().Next(10, 40)), new Point(new Random().Next(100, 120), new Random().Next(10, 30)));
graphics.DrawLine(new Pen(brushText, new Random().Next(1, 3)), new Point(new Random().Next(20, 50), new Random().Next(0, 10)), new Point(new Random().Next(100, 120), new Random().Next(30, 40)));
graphics.DrawString(text, font, brushText, 0, 10);

}
else//画备选答案
{
Point middle = new Point(25, 20);
graphics.TranslateTransform(middle.X, middle.Y);
//这里是360°随机旋转
graphics.RotateTransform(new Random().Next(0, 360));
var format = new StringFormat(StringFormatFlags.NoClip)
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
graphics.DrawString(text, font, brushText, 0, 0, format);

}
brushText.Dispose();
graphics.Dispose();
return ImageToBase64(image);
}

显示

直接调用GetCode方法就能返回验证码对象

下面是后台代码,应为正确答案是放在AnswerValue里的所以我先把取出来放Session里面,然后把值清空后再通过json返回给浏览器。

1
2
3
4
5
6
7
public string GetVerCode()
{
var code = new VerificationCode.Code().GetCode();
Session["VERCODE"] = code.AnswerValue;
code.AnswerValue = "";
return JsonConvert.SerializeObject(code);
}

现在来堆点html代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="form-group">
<ul class="vercode">
<li><img src=''/></li>
<li><img src=''/></li>
<li><img src=''/></li>
<li><img src=''/></li>
<li class="delete" onclick="delete_input()"></li>
</ul>
<div>
<img id="code-image"/> <a href="javascript:void(0);" onclick="load_vercode()">看不清?</a>
</div>
<ul class="vercode-anwser">
<li><img /></li>
<li><img /></li>
<li><img /></li>
<li><img /></li>
<li><img /></li>
<li><img /></li>
<li><img /></li>
<li><img /></li>
<li><img /></li>
</ul>
</div>

再来点js代码,这里只实现的图片上的效果,还没对数据验证(这里说说验证思路:每个图片对应一个ID,取到选择图片的ID用逗号分隔,然后与Session里的值对比)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script>
$(function () {
//加载验证码
load_vercode();
//绑定验证码点击事件
$(".vercode-anwser").find("img").on("click", null, function () {
$(".vercode").find("img[src='']:eq(0)").attr("src", $(this).attr("src"));
});
});

function load_vercode() {
$(".vercode").find("img").attr("src", "");
$.get("GetVerCode", function (data) {
var result = JSON.parse(data);
$("#code-image").attr("src", "data:image/png;base64," + result.CodeImg);
$(".vercode-anwser").find("img").each(function (index) {
$(this).attr("src", "data:image/png;base64," + result.Answer[index].Img);
});
});
}
//删除事件
function delete_input() {
$(".vercode").find("img[src!='']:last").attr("src", "");
}
</script>

到这里代码就差不多了,以上思路只是单纯的个人想法,有兴趣的朋友一起来讨论吧。

源代码在这里–>地址 http://pan.baidu.com/s/1eS5Mn30 密码:7iud

分享代码改变世界

LayUI Table 添加禁止选中

LayUI这几年比较流行,里面的Table组件也比较强大,但是前面的CheckBox没有禁止选中功能,今天就来试试,看看能不能给添加一个禁止选中功能。

Fork LayUI源码

  • LayUI项目地址
  • Clone到本地
  • 找到src里面lay下面的modules文件夹里面的table.js

添加字段标记

  • 参照这里我们也添加一个”IS_DISABLE”的标记
    2019-05-22-16-14-50

  • 添加完如下

    1
    2
    3
    4
    5
    config: {
    checkName: 'LAY_CHECKED', //是否选中状态的字段名
    indexName: 'LAY_TABLE_INDEX', //下标索引名
    disableName: 'LAY_DISABLED' //是否禁用
    } //全局配置项

修改代码逻辑

  • 找到区域模板里面的 lay-filter="layTableAllChoose"
    2019-05-22-14-55-15

  • 在后面添加如下代码

    1
    {{# if(item2[d.data.disableName]){ }} disabled {{# }; }} 
  • 添加列渲染
    2019-05-22-16-02-15

  • 修改选择逻辑
    2019-05-22-16-12-51
    2019-05-22-16-13-23

  • 详细修改请点这里

测试

  • 在数据里面添加 "LAY_DISABLED": true,
  • 来看看效果
    1234

源码

Asp.Net Mvc使用 WebUploader 多图片上传

来博客园有一个月了,哈哈。在这里学到了很多东西。今天也来试着分享一下学到的东西。希望能和大家做朋友共同进步。

  最近由于项目需要上传多张图片,对于我这只菜鸟来说,以前上传图片都是直接拖得控件啊,而且还是一次只能传一张。由于现在 项目用的是MVC,像Asp那样 拖控件 是不现实了。在我脑海中立刻就浮现出一个想法,网上一定有插件,哈哈。去网上一搜索,哇哦这么多。在众多的插件中我被百度的WebUploader吸引了。官网上写着:"WebUploader是由Baidu WebFE(FEX)团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件...”;就是这句里面的"简单"和"现代" 这两个词深深的吸引了我,当然官网提供的Demo也很酷,而且还是开源的,哇咔咔。既然决定用这个了,我就下了一份源代码下来。打开一看,什么gui ,居然是php的。当时的心情你们能理解么?于是我就抱着试一试的心态搜索了一下 "webuploader asp.net" 哈哈居然搜到到Demo了.Demo 是从 eflay 这位前辈的博客上看的。我借用了他的后台方法,由于前面js写的太复杂我没敢用。好了不扯废话了下面就来演示根据WebUploader官方提供的API和前面说的那位前辈的后台方法完成图片上传。

下载

-WebUploader

将下载到的压缩包里面的文件复制到自己的项目中

添加引用

1
2
3
4
5
6
<!--引入Jquery-->
<script src="~/Script/jquery-1.8.2.min.js"></script>
<!--引入Css-->
<link href="~/CSS/webuploader.css" rel="stylesheet" />
<!--引入Js-->
<script src="~/Script/webuploader.js"></script>

准备一个放图片的容器和一个上传按钮

1
2
<div id="fileList"></div> <!--这是存放图片的容器-->
<div class="cp_img_jia" id="filePicker"></div> <!--这是上传按钮-->

创建Web Uploader实例并监听事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<script type="text/javascript">

var applicationPath = window.applicationPath === "" ? "" : window.applicationPath || "../../";
$(function () {
var $ = jQuery,
$list = $('#fileList'),
// 优化retina, 在retina下这个值是2
ratio = window.devicePixelRatio || 1,
// 缩略图大小
thumbnailWidth = 90 * ratio,
thumbnailHeight = 90 * ratio,
// Web Uploader实例
uploader;
uploader = WebUploader.create({
// 选完文件后,是否自动上传。
auto: false,

// swf文件路径
swf: applicationPath + '/Script/Uploader.swf',

// 文件接收服务端。
server: applicationPath + '/Home/UpLoadProcess',

// 选择文件的按钮。可选。
// 内部根据当前运行是创建,可能是input元素,也可能是flash.
pick: '#filePicker',

//只允许选择图片
accept: {
title: 'Images',
extensions: 'gif,jpg,jpeg,bmp,png',
mimeTypes: 'image/*'
}
});

// 当有文件添加进来的时候
uploader.on('fileQueued', function (file) {
var $li = $(
'<div id="' + file.id + '" class="cp_img">' +
'<img>' +
'<div class="cp_img_jian"></div></div>'
),
$img = $li.find('img');


// $list为容器jQuery实例
$list.append($li);

// 创建缩略图
// 如果为非图片文件,可以不用调用此方法。
// thumbnailWidth x thumbnailHeight 为 100 x 100
uploader.makeThumb(file, function (error, src) {
if (error) {
$img.replaceWith('<span>不能预览</span>');
return;
}

$img.attr('src', src);
}, thumbnailWidth, thumbnailHeight);
});

// 文件上传过程中创建进度条实时显示。
uploader.on('uploadProgress', function (file, percentage) {
var $li = $('#' + file.id),
$percent = $li.find('.progress span');

// 避免重复创建
if (!$percent.length) {
$percent = $('<p class="progress"><span></span></p>')
.appendTo($li)
.find('span');
}

$percent.css('width', percentage * 100 + '%');
});

// 文件上传成功,给item添加成功class, 用样式标记上传成功。
uploader.on('uploadSuccess', function (file, response) {

$('#' + file.id).addClass('upload-state-done');
});

// 文件上传失败,显示上传出错。
uploader.on('uploadError', function (file) {
var $li = $('#' + file.id),
$error = $li.find('div.error');

// 避免重复创建
if (!$error.length) {
$error = $('<div class="error"></div>').appendTo($li);
}

$error.text('上传失败');
});

// 完成上传完了,成功或者失败,先删除进度条。
uploader.on('uploadComplete', function (file) {
$('#' + file.id).find('.progress').remove();
});

//所有文件上传完毕
uploader.on("uploadFinished", function ()
{
//提交表单

});

//开始上传
$("#ctlBtn").click(function () {
uploader.upload();

});

//显示删除按钮
$(".cp_img").live("mouseover", function ()
{
$(this).children(".cp_img_jian").css('display', 'block');

});
//隐藏删除按钮
$(".cp_img").live("mouseout", function () {
$(this).children(".cp_img_jian").css('display', 'none');

});
//执行删除方法
$list.on("click", ".cp_img_jian", function ()
{
var Id = $(this).parent().attr("id");
uploader.removeFile(uploader.getFile(Id,true));
$(this).parent().remove();
});

});


</script>

在Controller里新建一个Action用于保存图片并返回图片路径(这方法是 eflay 前辈博客上说的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public ActionResult UpLoadProcess(string id, string name, string type, string lastModifiedDate, int size, HttpPostedFileBase file)
{
string filePathName = string.Empty;

string localPath = Path.Combine(HttpRuntime.AppDomainAppPath, "Upload");
if (Request.Files.Count == 0)
{
return Json(new { jsonrpc = 2.0, error = new { code = 102, message = "保存失败" }, id = "id" });
}

string ex = Path.GetExtension(file.FileName);
filePathName = Guid.NewGuid().ToString("N") + ex;
if (!System.IO.Directory.Exists(localPath))
{
System.IO.Directory.CreateDirectory(localPath);
}
file.SaveAs(Path.Combine(localPath, filePathName));

return Json(new
{
jsonrpc = "2.0",
id = id,
filePath = "/Upload/" + filePathName
});

}

这样就大功告成了。由于是第一次写博客,里面如果有写的不详细或不对的地方,欢迎大家指点。希望能和大家一起进步。

Demo下载地址http://pan.baidu.com/s/1hqqvB0o