一、目的
最近在研究分布式系统,将部分服务拆分出去,只会python,所以用python的zerorpc写服务
二、代码
1.查找字体的函数
想指定pillow生成图片的文字大小,就要一起指定文字的字体,默认只搜索系统的字体,我手动添加了一个自定义目录到搜索范围内。
这个函数是从pillow里面复制出来的。
import os
import sys
from PIL.ImageFont import FreeTypeFont
# 以现在这个py文件为基点,向上两级,作为基础目录
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# 默认字体
DEFAULT_FONT = "simsunb.ttf"
def mytruetype(font=None, size=10, index=0, encoding="utf8", layout_engine=None):
def freetype(font):
return FreeTypeFont(font, size, index, encoding, layout_engine)
ttf_filename = os.path.basename(font)
# print(ttf_filename, "ttf_filename")
dirs = []
if sys.platform == "win32":
windir = os.environ.get("WINDIR")
if windir:
dirs.append(os.path.join(windir, "fonts"))
elif sys.platform in ("linux", "linux2"):
lindirs = os.environ.get("XDG_DATA_DIRS", "")
if not lindirs:
lindirs = "/usr/share"
dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")]
elif sys.platform == "darwin":
dirs += [
"/Library/Fonts",
"/System/Library/Fonts",
os.path.expanduser("~/Library/Fonts"),
]
# 就是这里,将基础目录下的"字体"文件夹添加到搜索范围内
dizhi = os.path.join(BASE_DIR, "字体")
dirs.append(dizhi)
# print(dirs, "所有要查找的路径")
ext = os.path.splitext(ttf_filename)[1]
# print(ext, "ext")
first_font_with_a_different_extension = None
for directory in dirs:
for walkroot, walkdir, walkfilenames in os.walk(directory):
# print(walkfilenames, "找到的文件名称")
for walkfilename in walkfilenames:
if ext and walkfilename == ttf_filename:
return freetype(os.path.join(walkroot, walkfilename))
elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename:
fontpath = os.path.join(walkroot, walkfilename)
if os.path.splitext(fontpath)[1] == ".ttf":
return freetype(fontpath)
if not ext and first_font_with_a_different_extension is None:
first_font_with_a_different_extension = fontpath
if first_font_with_a_different_extension:
return freetype(first_font_with_a_different_extension)
else:
# 兜底逻辑,如果没有找到指定的字体,就用默认字体再找一遍,
return mytruetype(font=DEFAULT_FONT, size=size, index=index, encoding=encoding, layout_engine=layout_engine)
2.主体逻辑
最后返回的是字符串
import base64
import random
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from 验证码服务.查找字体 import mytruetype
def save_img(image) -> str:
buffer = BytesIO()
image.save(buffer, "PNG") # 将Image对象转为二进制存入buffer。因BytesIO()是在内存中操作,所以实际是存入内存
buf_bytes = buffer.getvalue() # 从内存中取出bytes类型的图片
base64_data = base64.b64encode(buf_bytes) # 将bytes类型按base64进行编码,返回值还是bytes类型
buffer.close() # 用完了就关闭
return f"data:image/png;base64,{base64_data.decode('ascii')}"
class CreateImage:
draw = None
def __init__(self, code_str,
size=(120, 32),
img_type='PNG',
mode='RGB',
bg_color=(255, 255, 255),
fg_color=(60, 50, 255),
font_size=18,
font_type="Monaco.ttf",
draw_lines=True,
n_line=(1, 2),
draw_points=True,
point_chance=2) -> None:
self.code_str = code_str
self.size = size
self.img_type = img_type
self.mode = mode
self.bg_color = bg_color
self.fg_color = fg_color
self.font_size = font_size
self.font_type = font_type
self.draw_lines = draw_lines
self.n_line = n_line
self.draw_points = draw_points
self.point_chance = point_chance
# 宽和高
self.width, self.height = self.size
# 绘制干扰线
def create_line(self):
"""绘制干扰线条"""
# 干扰线条数
line_num = random.randint(*self.n_line)
for i in range(line_num):
# 起始点
begin = (random.randint(0, self.size[0]), random.randint(0, self.size[1]))
# 结束点
end = (random.randint(0, self.size[0]), random.randint(0, self.size[1]))
self.draw.line([begin, end], fill=(0, 125, 101))
# 绘制干扰点
def create_points(self):
"""绘制干扰点"""
# 大小限制在[0, 100]
chance = min(100, max(0, int(self.point_chance)))
for w in range(self.width):
for h in range(self.height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
self.draw.point((w, h), fill=(0, 125, 101))
# 每个字符前后以空格隔开
def create_strs(self):
strs = ' %s ' % ' '.join(self.code_str)
font = mytruetype("字魅行楷.ttf", 25) # 设置字体
self.draw.text((self.width / 15, self.height / 8), strs, font=font, fill=self.fg_color)
# 生成图片
def get_img(self) -> str:
# 创建图形
img = Image.new(self.mode, self.size, self.bg_color)
# 创建画笔
self.draw = ImageDraw.Draw(img)
if self.draw_lines:
self.create_line()
if self.draw_points:
self.create_points()
self.create_strs()
# 滤镜,边界加强(阈值更大)
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
return save_img(img)
3.服务
import zerorpc
from 验证码服务.服务 import CreateImage
class HandleModel():
def create_img(self, config=None):
if not isinstance(config, dict):
return "必须是个字典,其他的不行"
obj = CreateImage(**config)
return obj.get_img()
if __name__ == '__main__':
s = zerorpc.Server(HandleModel())
s.bind("tcp://0.0.0.0:2442")
print("开启服务")
s.run()
基本就是这样了