How to send back image/file in aiohttp

Viewed 2212

I need to know how to send image back in aiohttp. I coding a server for image resizing. I used aiohttp.web.FileResponse but it required saved file and it's a bit problem (a lot of files need to be saved on harddrive).

Is there any way to do it flexibly (without saving file)? From image bytes maybe or something? I read aiohttp documentation but it didn't do much.

Here what I tried to do:

  1. FileResponse

Here I've got to save it to send response back

image = tasks.get(key)  # PIL.Image
image.save('im_server/pil_{}.jpg'.format(key))  
resp = web.FileResponse(f'im_server/pil_{key}.jpg')
return resp
  1. StreamResponse

When I make request with this code, I've got file back (it's uploading on my computer) but it's not an image. If I try to open it as image, it says file damaged and cannot be opened :(

image = tasks.get(key)  # PIL.Image
resp = web.StreamResponse(status=200)
resp.headers['Content-Type'] = 'Image/JPG'
await resp.prepare(request)
await resp.write(image.tobytes())
return resp
2 Answers

You can use tempfile.SpooledTemporaryFile to do the saving job. It is designed to store temp files in the memory and will only save the file on disk if the file size exceeds max_size parameter. Notice that this parameter is 0 by default so you need to change it to a suitable size to avoid storing everything on disk. The usage is very simple, SpooledTemporaryFile will return a file_like object handle that you can write to it just like a regular file. Once you don't need it, just close it and it will be automatically removed from memory or disk. You may refer to the document to see more usages: https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile.

You can use io.BytesIO.

async def img_resize(req: web.Request):
    data = await req.post()
    url = data.get('url')
    width = data.get('width')
    height = data.get('height')

    if not all((url, width, height)):
        return web.HTTPNotFound()

    try:
        width = int(width)
        height = int(height)
    except ValueError:
        return web.HTTPError()

    async with ClientSession() as session:
        async with await session.get(url) as res:
            if res.status != 200:
                return web.HTTPNotFound()

            img_raw = await res.read()

    im = Image.open(BytesIO(img_raw))
    im = im.resize((width, height), Image.BICUBIC)

    stream = BytesIO()
    im.save(stream, "JPEG")

    return web.Response(body=stream.getvalue(), content_type='image/jpeg')
Related