0

While I am learning Flask, I wrote a small service that receives a image, resize and reduce it's quality.

To avoid write it to the disk and then delete it, I use a buffer, and it worked fine. But now I can't send it using flask send_file. I tried a few solutions that include wrap it using werkzeug FileWrapper and send using Response, but it also did not work. Also, it does't show any kind of error...

@app.route('/api/convert', methods=['POST'])
def convert():
    if 'image' not in request.files:
        return 'No image!'
    if request.files['image'].content_type not in ALLOWED_CONTENT:
        return 'Not an allowed image!'
    filename = request.files['image'].filename
    mimetype = request.files['image'].mimetype
    print(mimetype)
    buffer = io.BytesIO()
    request.files['image'].save(buffer)
    image = Image.open(buffer)
    w, h = resize_image(*image.size)
    image = image.resize((w, h), Image.ANTIALIAS)
    print(type(buffer))
    image.save(buffer,
               format=mimetype.split('/')[-1],
               optimize=True,
               quality=DEFAULT_QUALITY)
    return send_file(buffer,
                     mimetype=mimetype,
                     attachment_filename=filename,
                     as_attachment=True)

When I point to a file that exist in my system, it works fine...

UPDATE

It was pointed out that I was not using buffer.seek(0), after doing putting it on, i started to receive the image in my requests, but the image is far from what I expected.

For example, my test image is 5.2MB, when I save it to the disk instead of the buffer, it goes to 250KB, but when i try to save it to the buffer, and send it using send_file, it goes to 5.5MB...

@app.route('/api/convert', methods=['POST'])
def convert():
    if 'image' not in request.files:
        return 'No image!'
    if request.files['image'].content_type not in ALLOWED_CONTENT:
        return 'Not an allowed image!'
    filename = request.files['image'].filename
    mimetype = request.files['image'].mimetype
    buffer = io.BytesIO()
    request.files['image'].save(buffer)
    image = Image.open(buffer)
    w, h = resize_image(*image.size)
    buffer.seek(0)
    image = image.resize((w, h), Image.ANTIALIAS)
    image.save(buffer,
               format=mimetype.split('/')[-1],
               optimize=True,
               quality=DEFAULT_QUALITY)
    buffer.seek(0)
    return send_file(buffer,
                     mimetype=mimetype,
                     attachment_filename=filename,
                     as_attachment=True)

I am editing this question title and removing the tags for flask because it seems that my problem is just the lack of knowledge about io's BytesIO library.

UPDATE 2

I was working in another project when it came to my mind. What if I create a new buffer to save the image already modified?

And it worked.

@app.route('/api/convert', methods=['POST'])
def convert():
    if 'image' not in request.files:
        return 'No image!'
    if request.files['image'].content_type not in ALLOWED_CONTENT:
        return 'Not an allowed image!'
    filename = request.files['image'].filename
    mimetype = request.files['image'].mimetype
    buffer = io.BytesIO()
    buffer_final = io.BytesIO()
    request.files['image'].save(buffer)
    image = Image.open(buffer)
    w, h = resize_image(*image.size)
    image = image.resize((w, h), Image.ANTIALIAS)
    image.save(buffer_final,
               format=mimetype.split('/')[-1],
               optimize=True,
               quality=75)
    buffer_final.seek(0)
    return send_file(buffer_final,
                     mimetype=mimetype,
                     attachment_filename=filename,
                     as_attachment=True)

So, apparently I can't replace the content of the BytesIO buffer? Anyone knows what I am doing wrong? (yeah, I made it work, but I guess that other people would benefit from the same problem?)

  • change image to `BytesIO`, https://stackoverflow.com/questions/35710361/python-flask-send-file-stringio-blank-files – sahasrara62 Dec 10 '20 at 21:14
  • @sahasrara62 I don't think I understand. But you link show the use of seek(0), I tried it and it did return a image, but not optimized. It's clear that my buffer did not work as i imagine it would. – Guilherme Richter Dec 10 '20 at 21:39
  • well, try using different buffer/cache systems to avoid this, make a service/pipeline in your project which takes the image and uses that caching system to do the manipulation and return the pixeled image which will be serveable by your server. – sahasrara62 Dec 10 '20 at 23:58
  • Try a `buffer.truncate()` after the `image.save(buffer,...)` then call `buffer.seek(0)` and `send_file(buffer,...)`. – Justin Ezequiel Dec 11 '20 at 19:14
  • @JustinEzequiel I tried that too (i just retry, just to be sure) the image coming is not the saved version. – Guilherme Richter Dec 11 '20 at 19:29
  • Include all your imports so we can test too. – Justin Ezequiel Dec 11 '20 at 19:32
  • @JustinEzequiel import io import sys import logging from PIL import Image from flask import Flask, send_file, request – Guilherme Richter Dec 11 '20 at 20:10

1 Answers1

0

Tested on my machine and truncate() after save(...) works just fine.

import math
import shutil
from PIL import Image
from io import BytesIO

src = r"C:\Users\justin\Desktop\test.jpg"

f = open(src, 'rb')
buffer = BytesIO()
shutil.copyfileobj(f, buffer)
print(f.tell(), buffer.tell())
f.close()

buffer.seek(0)
image = Image.open(buffer)
image.show()
print(buffer.tell())
print(image.size)

w, h = image.size
w, h = math.floor(w*0.75), math.floor(h*0.75)
print(w, h)

smaller = image.resize((w, h), Image.ANTIALIAS)
smaller.show()
print(smaller.size)

buffer.seek(0)
smaller.save(buffer, format='JPEG', optimize=True)

print(buffer.tell())
buffer.truncate()

buffer.seek(0)
out = Image.open(buffer)
out.show()
print(out.size)

Justin Ezequiel
  • 4,686
  • 2
  • 10
  • 13
  • OMG I am so dumb. I put the truncate() BEFORE the save thinking it would "erase" the buffer. Well, it works perfectly now. Thank you very much, and sorry for waste your time when you clearly give me the right answer in the comment. – Guilherme Richter Dec 11 '20 at 20:46