python3生成图片验证码并在angluar中显示
python生成验证码基本套路,使用Pillow(python2可以用PIL)生成文字和干扰线。
参考文章:https://blog.fudenglong.site/2017/09/21/python3%E7%94%9F%E6%88%90%E9%AA%8C%E8%AF%81%E7%A0%81/
该文章代码生成了中文验证码,在此基础上我增加了英文数字验证码的选项,并区分了Unicode和gbk2312字符集(Unicode字符集包含中文太多,会出现过多生僻字)。由于在实际应用当中生成图片的情况较少,一般都是放到前端页面显示,故增加了生成图片base64字符串,便于接口传输。
代码
import random
import string
import base64
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
class ImageChar(object):
def __init__(self, font_color=(0, 0, 0),
size=(160, 60),
font_path='/ui/src/assets/fonts/simsun.ttc',
bg_color=(255, 255, 255),
font_size=30):
self.size = size
self.font_path = font_path
self.bg_color = bg_color
self.font_size = font_size
self.font_color = font_color
self.font = ImageFont.truetype(self.font_path, self.font_size)
self.image = Image.new('RGB', size, bg_color)
def rotate(self):
self.image.rotate(random.randint(0, 30), expand=0)
def draw_text(self, pos, txt, fill):
draw = ImageDraw.Draw(self.image)
draw.text(pos, txt, font=self.font, fill=fill)
@staticmethod
def rand_rgb():
return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
def rand_point(self):
width, height = self.size
return random.randint(0, width), random.randint(0, height)
def rand_line(self, num=10):
draw = ImageDraw.Draw(self.image)
for i in range(num):
draw.line([self.rand_point(), self.rand_point()], self.rand_rgb())
@staticmethod
def random_chinese(code='gbk2312'):
if code == 'unicode':
return chr(random.choice(range(0X4E00, 0X9FA5)))
elif code == 'gbk2312':
head = random.randint(0xb0, 0xf7)
body = random.randint(0xa1, 0xf9)
val = "%x%x" % (head, body)
return bytes.fromhex(val).decode('gb2312')
else:
raise ValueError('code error')
@staticmethod
def random_char():
return random.choice(string.ascii_letters + string.digits)
def write_char(self, num=4, is_chinese=False):
char_str = ""
width, height = self.size
gap, start = int(width / num - self.font_size), 0
get_char_func = self.random_chinese if is_chinese else self.random_char
for i in range(num):
char = get_char_func()
char_str += char
x = start + self.font_size * i + random.randint(0, gap) + gap * i
self.draw_text((x, random.randint(0, int((height - self.font_size) / 2))), char, self.rand_rgb())
self.rotate()
self.rand_line(random.randint(10, 15))
return char_str
def output(self, path):
self.image.save(path)
def to_base64(self):
buffered = BytesIO()
self.image.save(buffered, format="JPEG")
img_str = base64.b64encode(buffered.getvalue())
return "data:image/jpeg;base64," + img_str.decode()
@classmethod
def example(cls):
instance = cls(size=(160, 60), font_size=28)
instance.write_char(4)
instance.output("test.png")
@classmethod
def create_chinese_captcha(cls):
instance = cls(font_path='%s/ui/src/assets/fonts/simsun.ttc' % (
settings.BASE_DIR, ))
char_str = instance.write_char(4, True)
img_str = instance.to_base64()
return char_str, img_str
@classmethod
def create_char_captcha(cls):
instance = cls(font_path='%s/ui/src/assets/fonts/Arial.ttf' % (
settings.BASE_DIR, ))
char_str = instance.write_char(4)
img_str = instance.to_base64()
return char_str, img_str
- 注意如果使用中文需使用中文字体,否则不能显示
前端使用
前端使用angular框架,基本代码如下:
html
<a nz-col [nzSpan]="8" (click)="getCaptchaImage()">
<img [src]="captchaImage" height="40px">
</a>
TS
...
captchaImage: string;
...
getCaptchaImage() {
this.captchaApi.getBase64Img().subscribe( response => {
this.captchaImage = response['image']
});
}
...
后端逻辑
每次请求生成验证码图片的时候,可以将验证码字符串存储在session中,以便做验证码校验。