Guilherme Richter
Guilherme Richter

Reputation: 113

Can't save changes to BytesIO buffer

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?)

Upvotes: 0

Views: 971

Answers (1)

user5386938
user5386938

Reputation:

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)

Upvotes: 1

Related Questions