美文网首页程序员
Python PIL库生成验证码保存内存中

Python PIL库生成验证码保存内存中

作者: 嘿嘿_小于同学 | 来源:发表于2017-06-03 16:45 被阅读552次
1、前言

最近要用python生成验证码,在网上搜索了以后大部分都是使用的pythonPIL库的,但是大部分就是将生成的验证码图片保存到本地,然后在前端使用如下格式显示;

![](验证码图片的路径)

补充一下环境是win10、python2.7

2、生成验证码

关于生成验证码可以参照廖大的http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00140767171357714f87a053a824ffd811d98a83b58ec13000
或者这个http://www.jianshu.com/p/7b39db561e75
我是使用后者的代码修改自用的。
注意:如何安装依赖的库就不说了,主要是在运行过程可能会遇到如下错误
(1)ImportError

File "D:\Python26\Lib\site-packages\PIL\ImageFont.py", line 34, in __getattr__
  raise ImportError("The _imagingft C module is not installed")

解决方法是卸载PIL安装pillow

pip install PIL
pip install pillow

(2)无法加载字体

IOError: cannot open resource

解决方法是将win10字体的绝对地址给出C:\Windows\Fonts\Arial.ttf

3、问题

看了几篇博文写的都是先生成验证码图片,然后在在前端显示,由于验证码很小,没必要保存到本地,这样可以省掉一个页面上的HTTP请求,坏处就是浏览器不能缓冲验证码图片,其实我们没必要要浏览器来缓冲它,因为验证码具有变化性。所以我们在HTML页面上直接从内存中读取验证码图片。
有一些网页上有些图片后面跟着一大串符号如

data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAAAEAAAAkCAYAAABIdFAMAAA
GXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAH
JREFUeNo8zjsOxCAMBFB/
KEAUFFR0Cbng3nQPw68ArZdAlOZppPFIBhH5EAB8b+Tlt9MYQ6i1BuqFaq1CKSVcxZ2Acs6406KUgpt5/
LCKuVgz5BDCSb13ZO99ZOdcZGvt4mJjzMVKqcha68iIePB86GAiOv
CDADlIUQBs7MD3wAAAABJRU5Er

这些是Data URI Scheme,目的是将一些小数据,直接嵌入到网页中,而不从外部文件加载。
所以我们可以将生成的验证码文件编码之后发送到前端。

4、所有代码

(1)、生成验证码(代码来自搬运)

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from webapp.config import DevConfig

try:
    import cStringIO as StringIO
except ImportError:
    import StringIO

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母
_upper_cases = "ABCDEFGHJKLMNPQRSTUVWXY"  # 大写字母
_numbers = "1234567890"  # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))  # 生成允许的字符集合
default_font = DevConfig.FONTPATH  # 验证码字体,在配置文件中定义


# 生成验证码接口120 30
def generate_verify_image(size=(140, 40),
                          chars=init_chars,
                          img_type="GIF",
                          mode="RGB",
                          bg_color=(255, 255, 255),
                          fg_color=(0, 0, 255),
                          font_size=18,
                          font_type=default_font,
                          length=4,
                          draw_lines=True,
                          n_line=(1, 2),
                          draw_points=True,
                          point_chance=2,
                          save_img=False):
    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 xrange(width):
            for h in xrange(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)  # 滤镜

    mstream = StringIO.StringIO()
    img.save(mstream, img_type)
    
    return mstream.getvalue().encode('base64'), strs

(2)、视图

@auth_blueprint.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        # 验证码不区分大小写
        if 'code_text' in session and session['code_text'] != form.verification_code.data.lower():
            flash(u'验证码错误!')
            image_stream, code_text = generate_verify_image()
            session['code_text'] = code_text.lower()
            return render_template('login.html', form=form, image_stream=image_stream)
        user = User.query.filter_by(username=form.user_name.data).first()
        if user is not None and user.check_password(form.password.data):
            login_user(user, form.remember_me.data)
            session['user_name'] = user.username

            identity_changed.send(
                current_user._get_current_object(),
                identity=Identity(user.id)
            )

            return redirect(url_for('main.home'))
        flash(u"用户名或密码错误")
    image_stream, code_text = generate_verify_image()
    session['code_text'] = code_text.lower()
    return render_template('login.html', form=form, image_stream=image_stream)

(3)、前端

<form action="" method="post" class="form" role="form">
                {{ form.hidden_tag() }}
                <div class="form-group required">
                    {{ form.user_name.label(class="control-label") | safe }}
                    {{ form.user_name(class="form-control", required=True) }}
                </div>
                <div class="form-group required">
                    {{ form.password.label(class="control-label") | safe }}
                    {{ form.password(class="form-control", required=True) }}
                </div>
                {% if form.errors.verification_code %}
                    <div class="form-group has-error required">
                        {{ form.verification_code.label(class="control-label") | safe }}
                        {{ form.verification_code(class="form-control", required=True) }}
                        {% for error in form.errors.verification_code %}
                            <p class="help-block">{{ error }}</p>
                        {% endfor %}
                    </div>
                {% else %}
                    <div class="form-group required">
                        {{ form.verification_code.label(class="control-label") | safe }}
                        <label class="control-label"><img
                                src="data:image/gif;base64,{{ image_stream }}"></label>
                        {{ form.verification_code(class="form-control", required=True) }}
                    </div>
                {% endif %}
                <div class="form-group">
                    {{ form.remember_me.label(class="control-label") }}
                    {{ form.remember_me }}
                </div>

                {{ form.submit(class="btn btn-info submit") }}
            </form>

END

相关文章

网友评论

    本文标题:Python PIL库生成验证码保存内存中

    本文链接:https://www.haomeiwen.com/subject/wlhmfxtx.html