python 使用 io.BytesIO 内存文件加速图片生成服务
服务使用了 python3.7,sanic 框架,使用临时文件的大概代码如下:
import os import sys import logging import traceback import sanic import asyncio import matplotlib.pyplot as plt app = sanic.Sanic(My color patch service, load_env=MY_COLORPATCH_) def _worker(request): data = request.pop(data) longitudes = [r[0] for r in data] latitudes = [r[1] for r in data] values = [r[2] for r in data] fig = plt.figure(figsize=(10, 10), dpi=90) draw image by matplotlib image_file = os.path.realpath(out1.png) fig.savefig(image_file, transparent=True) plt.close(fig) return image_file def _worker_wrap(request): try: return _worker(request) except KeyboardInterrupt as e: raise e # rethrow the Ctrl+C Exception, for terminate the worker process except: return traceback.format_exc(limit=5) @app.route(/gen_color_patches, {POST}) async def make_color_patch(request): loop = asyncio.get_event_loop() resp = await loop.run_in_executor(None, _worker_wrap, request.json) if os.path.exists(resp): return await sanic.response.file_stream(resp, mime_type=image/png) return sanic.response.json({code: 1, msg: resp}, status=500) if __name__ == __main__: logging.basicConfig(level=logging.INFO, format=%(asctime)s-%(levelname)s: %(message)s) address_bind = sys.argv[1] if len(sys.argv) > 1 else localhost port = sys.argv[2] if len(sys.argv) > 2 else 8000 app.run(host=address_bind, port=int(port), workers=1)
因为生成临时文件需要IO时间,所以想省去写和读文件开销,所以改写如下:
def _worker(request): 以上都未变 image_file = io.BytesIO() fig.savefig(image_file, transparent=True) plt.close(fig) return image_file async def make_color_patch(request): loop = asyncio.get_event_loop() resp = await loop.run_in_executor(None, _worker_wrap, request.json) if isinstance(resp, str): return sanic.response.json({code: 1, msg: resp}, status=500) return sanic.response.raw(resp.getbuffer(), content_type=image/png)
临时的测试脚本也一并改写,移去了下载文件后写临时文件再读文件的步骤:
import os import io import json import requests import matplotlib.pyplot as plt data_file = rD:projects预报数据.json with open(data_file, r, encoding=utf-8) as f: data = json.load(f) params = { adcode: 450000, width: 1080, height: 1000, data: [(float(d[longitude]), float(d[latitude]), float(d[tmp])) for d in data], } resp = requests.post(http://127.0.0.1:8000/gen_color_patches, json=params) from PIL import Image img = Image.open(io.BytesIO(resp.content)) plt.imshow(img) plt.show()
BytesIO 作为一个 file-like object 实现,用起来确实很方便