图片验证码
页面显示图片的两种方式
下面的2个img标签都可以返回图片。
第一个直接就能获取静态文件,第二个现在并不能返回图片,还有配合个处理函数:
# urls.py 文件,写一个url的对应关系
path('getImg/'.lower(), views.get_img),
# views.py 文件,对应的处理函数
def get_img(request):
data = open('static/imgs/test.gif', 'rb').read()
return HttpResponse(data)
上面的处理函数,这里看到了HttpResponse的另外一个用法,之前都是返回的字符串。这里返回的是二进制。
第一个方法必须要有一个文件路径,img标签直接去获取这个文件。而第二个方法只需要返回文件的内容就可以了,甚至可以没有这个文件。
生成验证码图片
这里需要依赖2个东西:Pillow模块,字体文件(.ttf文件)
安装Pillow模块:
pip install Pillow
字体文件:windows系统的 C:\Windows\Fonts 目录下有很多字体文件,可以复制一个合适的拿来用。
整个功能就是一个组件,这里在项目目录下创建一个utils文件,统一来存放用的的组件或者工具。把下面的生成验证码的函数以及用到的字体文件都放到utiles文件夹下。
下面是现成的可以生成验证码图片的函数:
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper() # 大写字母
_numbers = ''.join(map(str, range(3, 10))) # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
def create_validate_code(size=(120, 30),
chars=init_chars,
img_type="GIF",
mode="RGB",
bg_color=(255, 255, 255),
fg_color=(0, 0, 255),
font_size=18,
font_type="utils/Monaco.ttf",
length=4,
draw_lines=True,
n_line=(1, 2),
draw_points=True,
point_chance=2):
"""
@todo: 生成验证码图片
@param size: 图片的大小,格式(宽,高),默认为(120, 30)
@param chars: 允许的字符集合,格式字符串
@param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
@param mode: 图片模式,默认为RGB
@param bg_color: 背景颜色,默认为白色
@param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
@param font_size: 验证码字体大小
@param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
@param length: 验证码字符个数
@param draw_lines: 是否划干扰线
@param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
@param draw_points: 是否画干扰点
@param point_chance: 干扰点出现的概率,大小范围[0, 100]
@return: [0]: PIL Image实例
@return: [1]: 验证码图片中的字符串
"""
width, height = size # 宽高
# 创建图形
img = Image.new(mode, size, bg_color)
draw = ImageDraw.Draw(img) # 创建画笔
def get_chars():
"""生成给定长度的字符串,返回列表格式"""
return random.sample(chars, length)
def create_lines():
"""绘制干扰线"""
line_num = random.randint(*n_line) # 干扰线条数
for i in range(line_num):
# 起始点
begin = (random.randint(0, size[0]), random.randint(0, size[1]))
# 结束点
end = (random.randint(0, size[0]), random.randint(0, size[1]))
draw.line([begin, end], fill=(0, 0, 0))
def create_points():
"""绘制干扰点"""
chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0))
def create_strs():
"""绘制验证码字符"""
c_chars = get_chars()
strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开
font = ImageFont.truetype(font_type, font_size)
font_width, font_height = font.getsize(strs)
draw.text(((width - font_width) / 3, (height - font_height) / 3),
strs, font=font, fill=fg_color)
return ''.join(c_chars)
if draw_lines:
create_lines()
if draw_points:
create_points()
strs = create_strs()
# 图形扭曲参数
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大)
return img, strs
代码说明:
准备好字体文件,这里也放到utils文件夹下,并在设置里引入字体文件 font_type="utils/Monaco.ttf",
。
整个模块只有一个函数create_validate_code()。另外内部还有几个函数,但是都是在create_validate_code函数内部使用的。使用的时候只需要调用create_validate_code()方法,然后可以获取到2个返回值,一次是图片的对象和对应的验证码的字符串。
返回验证码图片
重写get_img处理函数,调用上面的模块,返回图片对象的二进制数据给前端的img标签:
# views.py 文件
from io import BytesIO # 字节的IO操作
from utils.check_code import create_validate_code
def get_img(request):
img, code = create_validate_code()
print(img, code) # img是图片对象,code是对应的字符串
# file = open('img.png', 'wb') # 打开文件
# img.save(file, 'PNG') # 传入文件句柄,保存到文件,后面的参数是图片的格式
stream = BytesIO() # 相当于打开了一个文件
img.save(stream, 'PNG') # 这里就不需要生成文件了,而是放在内存里
# request.session['checkCode'] = code # 把验证码保存到session中
return HttpResponse(stream.getvalue())
这里还用到了BytesIO,因为要在内存中直接做读写操作。数据读写不一定是文件,也可以在内存中读写。StringIO顾名思义就是在内存中读写str。StringIO操作的只能是str,如果要操作二进制数据,就需要使用BytesIO。StringIO和BytesIO是在内存中操作str和bytes的方法,使得和读写文件具有一致的接口。
点击图片刷新
目前需要刷新整个页面才能刷新图片,实际应用中需要点击图片,只刷新验证码图片,页面的其他内容不变化。
这里需要给img标签绑定一个onclick事件,通过这个事件给img标签的src赋值。但是这里会有一个问题,浏览器的机制是只有src的值发生了变化才会发起一个新的请求,这就要求src的值需要和之前的值不同,并且还不能改变请求的地址。下面是实现点击刷新的方法:
这里用了一个老师号称机智的办法。get请求中的url会用到问号(?),用来分隔请求地址和参数的。这里每一次点击都会在后面添加一个问号,这样src的值变化了,但是请求的地址不变
富文本编辑框
常见的这类插件有:CKEditor、UEEditor、TinyEditor、KindEditor(下面要讲的)。
讲师的博客地址:http://www.cnblogs.com/wupeiqi/articles/6307554.html
KindEditor官网(中文):http://kindeditor.net/demo.php
安装
首先去官网下载,然后把文件夹复制到static静态文件目录下。您可以根据需求删除以下目录后上传到服务器:
- asp - ASP程序
- asp.net - ASP.NET程序
- php - PHP程序
- jsp - JSP程序
- examples - 演示文件
上面这些都是各种语言的示例,所以可以不要。
基本用法
首先在再页面上放一个textarea标签。这个博客的编辑器里绝对不能写textarea标签,所以代码里就故意写错了:
<多行文本 id="editor_id" name="content" style="width:700px;height:300px;">多行文本>
在有些浏览器上不设宽度和高度可能显示有问题,所以最好设一下宽度和高度。宽度和高度可用inline样式设置,也可用编辑器初始化参数设置。这里先用了前者。
KindEditor.create()方法就是进行编辑器初始化的。第一个参数是一个CSS选择器。第二个参数可以不写,这里写了个空字典和不写一样。是对编辑器进行配置的。
下面是简单的对高度和宽度进行了设置:
更多的参数设置还是看中文的官方文档吧:http://kindeditor.net/docs/option.html
上传文件
编辑器初始化的部分可以这么写,这里用了items参数把别的按钮都去掉了。主要是设置2个参数,一个是上传文件的url。另一个可以在上传的时候添加额外的参数,这里要把csrf_token加上:
服务端收到请求后,要按下面的JSON格式给客户端返回数据。官方文档:http://kindeditor.net/docs/upload.html
# 成功时
{
"error" : 0,
"url" : "http://www.example.com/path/to/file.ext"
}
# 失败时
{
"error" : 1,
"message" : "错误信息"
}
这里可以进控制台查看前端生成的html,使用的是iframe和form,标签内部还有很多别的标签就不贴了:
服务器端要写好对应的处理函数:
# urls.py 文件,url的对应关系
path('upload/', views.upload),
# views.py文件,对应的处理函数
import json
def upload(request):
print(request.GET.get('dir')) # 打印收到的文件的上传方式。image 或 file,另外还有flash等等
print(request.FILES) # 打印收到的文件对象
ret = {'error': 0, # 0是成功,1是有错误
'url': '/static/imgs/test.gif', # 返回图片保存的url,用来给前端生成预览的
}
return HttpResponse(json.dumps(ret))
这里先不保存,只是把接收到的内容打印出来,返回一个表示上传成功的信息。信息里包含一个url,这个url是服务器端保存图片的路径,是用来给客户端浏览器生成预览的。这里暂时没保存图片,先把服务器上已经有的一张图片的url返回,让客户端可以生成一个预览。
文件空间管理
这个编辑器还提供文件管理的功能,官方的叫法是浏览远程服务器(FileManager)。需要设置下面的2个参数:
- allowFileManager :true时显示浏览远程服务器按钮。默认值: false
- fileManagerJson :指定浏览远程图片的服务器端程序。和上传文件的url的参数一样,设置好url,然后去后端写好处理函数
后端的处理函数就不展开了,不过讲师的博客里有,这里就只留个钩子吧。
MarkDown编辑框
我这里试了下这个Editor.md :https://pandao.github.io/editor.md/index.html
安装
首先去官网下载,然后把文件夹复制到static静态文件目录下。
在页面里写上下面的代码就可以使用默认的设置了:
上面在外层的div是自己加上的,主要是设置一个高度,因为默认的初始化的时候高度和宽度都是100%。所以设置一下高度的话也可以去掉自己的div。
设置
基本的初始化设置有下面这些,从源码里找到的:
mode : "gfm", //gfm or markdown
name : "", // Form element name
value : "", // value for CodeMirror, if mode not gfm/markdown
theme : "", // Editor.md self themes, before v1.5.0 is CodeMirror theme, default empty
editorTheme : "default", // Editor area, this is CodeMirror theme at v1.5.0
previewTheme : "", // Preview area theme, default empty
markdown : "", // Markdown source code
appendMarkdown : "", // if in init textarea value not empty, append markdown to textarea
width : "100%",
height : "100%",
path : "./lib/", // Dependents module file directory
pluginPath : "", // If this empty, default use settings.path + "../plugins/"
delay : 300, // Delay parse markdown to html, Uint : ms
autoLoadModules : true, // Automatic load dependent module files
watch : true,
placeholder : "Enjoy Markdown! coding now...",
gotoLine : true,
codeFold : false,
autoHeight : false,
autoFocus : true,
autoCloseTags : true,
searchReplace : true,
syncScrolling : true, // true | false | "single", default true
readOnly : false,
tabSize : 4,
indentUnit : 4,
lineNumbers : true,
lineWrapping : true,
autoCloseBrackets : true,
showTrailingSpace : true,
matchBrackets : true,
indentWithTabs : true,
styleSelectedText : true,
matchWordHighlight : true, // options: true, false, "onselected"
styleActiveLine : true, // Highlight the current line
dialogLockScreen : true,
dialogShowMask : true,
dialogDraggable : true,
dialogMaskBgColor : "#fff",
dialogMaskOpacity : 0.1,
fontSize : "13px",
saveHTMLToTextarea : false,
disabledKeyMaps : [],
这里有一些详细的说明:https://pandao.github.io/editor.md/examples/index.html。
下载下来的examples文件夹里也有一份一样的。
自定义工具栏
官方文档里有的内容就不反复说明了,初始化的时候给toolbarIcons这个参数赋值,可以是列表。也可以是字符串(只能是full, simple, mini),不过也可以自定义一个名字,然后用字符串引入:
还可以自己创建按钮,偷懒不搞图标的话,也可以直接使用文字,还能给自定义的按钮添加事件:
$(function () {
editormd.toolbarModes.steed = ["undo", "redo", "|", "bold", "del", "italic", "quote",
"||", "top", "submit", "help", "info"];
var editor = editormd("editormd", {
path : "/static/editor.md-master/lib/",
toolbarIcons: 'steed',
toolbarIconTexts: {top: "回到顶部", submit: '提交'},
toolbarHandlers: {
top: function () {
alert('回到顶部');
}
},
});
})
自己以的方法了提供了一下4个对象可以使用,具体还是看官方的例子把:
- @param {Object}, cm: CodeMirror对象
- @param {Object}, icon: 图标按钮jQuery元素对象
- @param {Object}, cursor: CodeMirror的光标对象,可获取光标所在行和位置
- @param {String}, selection: 编辑器选中的文本
获取文本
这里主要讲下面3个方法
.getMarkdown()
: 获取MarkDown文本.getHTML()
: 获取HTML文本,需要开启saveHTMLToTextarea: true,
。.setMarkdown(md)
:填入MarkDown文本
下面的例子自定义了2个按钮,分别能获取到文本的MarkDown格式以及HTML格式的内容:
{% load static %}
另外上面的例子的最后一个 $.get()
方法是获取到文本后,在回调函数里调用editor.setMarkdown(md);
填写到文本框内。
获取MarkDown的方法获取到文本后,就可以实现上传到服务器的功能了。
获取HTML的方法可以获取到HTML的文本,不过貌似没什么用,要把MarkDown文本直接展示在HTML上有另外的转化方法。
自动填入文本框,则可以实现文档的修改的功能。
显示MarkDown
之前将编辑器的内容上传并保存到数据库,如果上传的内容是 .getMarkdown()
获取的Markdown文本。那么现在要做一个展示的页面,将数据库中的内容显示到页面上,并且展示的应该是html格式:
{% load static %}
上面不知道为什么要引入那么多js,不过一个都不用试下来是无法显示的。官网的例子里也引用那么多js,这里就全部引用了。
另外,这里的markdownToHTML没有加参数,使用的都是默认设置。如果需要调整,那么第二个参数传入一个字典进行设置: