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 实现,用起来确实很方便

经验分享 程序员 微信小程序 职场和发展