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


    一、目的

    最近在研究分布式系统,将部分服务拆分出去,只会python,所以用python的zerorpc写服务
    
    • 1

    二、代码

    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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    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)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    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()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    基本就是这样了

  • 相关阅读:
    车载电子电器架构 —— 售后诊断开发
    【数学】分数到小数 巧用MAP获取循环小数
    LeetCode每日一题(2149. Rearrange Array Elements by Sign)
    1月笔记本电脑行业分析:多品牌下滑但ThinkPad逆势增长!
    【Harmony OS】【ArkUI】ets开发 创建视图与构建布局
    C++常见设计模式
    C++特性——命名空间、I/O流以及缺省参数
    入门【网络安全/黑客】启蒙教程
    详解 Spring Boot 项目中的日志文件
    订单正逆向流程
  • 原文地址:https://blog.csdn.net/qq_43656607/article/details/133946766