关于在Django中用Pillow生成图形验证码的记录

最近因为业务需求,需要重写图形验证码部分。
使用的是Python3.6,代码很简单,看一眼基本就知道其中原理,这里仅作记录。

from PIL import (
    Image, ImageDraw, ImageFont, ImageFilter
)
from django.core.cache import cache
from django.conf import settings
from io import BytesIO
import os, random, string, time


class Captcha:
    """
        TODO: 自定义生成图形验证码
        using:  captcha 获取图形验证码
        using: verify_captcha 验证图形验证码
    """

    def __init__(self):
        self._code = string.ascii_uppercase + string.digits
        self._width = 100  # 图片宽
        self._height = 40  # 图片高
        self._bits = 4
        self._draw_line = True  # 干扰线
        self._line_num = (1, 5)  # 干扰线数量
        self._bgcolor = random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)  # 背景颜色
        self._font_path = os.path.join(settings.BASE_DIR, 'captcha/fonts/Vera.ttf')
        self.captcha_code = self._generate_shuffle_str()

    # 生成随机字符串
    def _generate_shuffle_str(self):
        shuffle_list = ','.join(self._code).split(',')
        random.shuffle(shuffle_list)
        return ''.join(shuffle_list[:self._bits])

    # 生成图像
    def _generate_image(self):
        image = Image.new('RGBA', (self._width, self._height), self._bgcolor)  # 画布
        font = ImageFont.truetype(self._font_path, 24)  # 用到的字体
        draw = ImageDraw.Draw(image)  # 画笔
        text = self._generate_shuffle_str()

        # 在画布上画字着色
        for i in range(len(text)):
            font_width, font_height = font.getsize(text[i])
            draw.text((self._width / self._bits * (i + 1) - font_width,
                       (self._height - font_height) / random.randint(2, self._bits)),
                      text[i],
                      font=font, fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))

        # 画上干扰线
        if self._draw_line:
            self._append_line(draw)
        # 画上躁点
        self._append_points(draw)
        # 应用图形变换
        image = image.transform((self._width, self._height),
                                Image.AFFINE,
                                (1, 0, 0, 0, 1, 0),
                                Image.BILINEAR)  # 创建扭曲
        image = image.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 汉斯滤镜, 边界加强

        return image

    # 追加躁点
    def _append_points(self, draw):
        chance = min(100, max(0, 5))
        for w in range(self._width):
            for h in range(self._height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    # 追加干扰线
    def _append_line(self, draw):
        for _ in range(random.randint(*self._line_num)):
            begin = random.randint(0, self._width), random.randint(0, self._height)
            end = random.randint(0, self._width), random.randint(0, self._height)
            draw.line([begin, end], fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))

    def captcha(self):
        buf = BytesIO()
        im = self._generate_image()
        tk = self._cache_captcha()
        im.save(buf, 'JPEG')
        im.close()
        buf.seek(0)
        return tk, buf

    # 临时记录图形验证码
    def _cache_captcha(self):
        timestamp_key = int(time.time() * 100000000) + random.randint(10, 99)
        cache.set(timestamp_key, self.captcha_code, 300)
        return timestamp_key

    # 验证图形验证码
    def verify_captcha(self, timestamp_key=None, captcha=None):
        if timestamp_key and captcha and captcha == cache.get(timestamp_key):
            # del the cache data
            cache.delete(timestamp_key)
            return True
        return False

因为我的设计验证是key:value形式,所以要把key也传给移动端,这里我使用了下面的方法:

from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from .captcha import Captcha
from base64 import b64encode


@csrf_exempt
def get_captcha(request):
    captcha = Captcha()
    tk, im_buf = captcha.captcha()
    # return HttpResponse(im_buf, content_type='image/jpeg')
    return JsonResponse({'recode': 1,
                         'remsg': '获取成功!',
                         'data': {'timestamp': tk, 'captcha': b64encode(im_buf.read()).decode('utf-8')}})

注释的部分用于在浏览器里面查看图片验证码。
这里在BytesIO那里踩了点坑,主要还是怪自己学艺不精。

-- 路漫漫其修远兮,吾将上下而求索。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,651评论 18 139
  • Web 端中,为了防止恶意的流量攻击或者为了防止自动化提交,都会在页面中引入验证码。验证码一般是一些加入了干扰线...
    Kerwong阅读 5,771评论 0 7
  • 这本书讲一个千年小镇龙盏的故事,辛七杂,辛欣来,安平,王秀满,安雪儿等人,但这个故事很精彩,也很曲折,我从来没看过...
    人生何梦阅读 259评论 0 0
  • 每个人,常常在想:如果有时间我要陪陪家人,如果有机会我要出去走走,如果有可能我要做件大事!却总是由于种种原因而未能...
    青苋阅读 689评论 0 0