用python的zerorpc写一个验证码的rpc服务

一、目的

最近在研究分布式系统,将部分服务拆分出去,只会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()

基本就是这样了

相关推荐
凤枭香2 分钟前
Python OpenCV 傅里叶变换
开发语言·图像处理·python·opencv
ULTRA??5 分钟前
C加加中的结构化绑定(解包,折叠展开)
开发语言·c++
测试杂货铺9 分钟前
外包干了2年,快要废了。。
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
艾派森13 分钟前
大数据分析案例-基于随机森林算法的智能手机价格预测模型
人工智能·python·随机森林·机器学习·数据挖掘
远望清一色22 分钟前
基于MATLAB的实现垃圾分类Matlab源码
开发语言·matlab
confiself31 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
小码的头发丝、39 分钟前
Django中ListView 和 DetailView类的区别
数据库·python·django
XiaoLeisj43 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
杜杜的man1 小时前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*1 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go