【问题标题】:Can't save changes to BytesIO buffer无法保存对 BytesIO 缓冲区的更改
【发布时间】:2021-03-22 07:58:49
【问题描述】:

在学习 Flask 时,我编写了一个小型服务,用于接收图像、调整大小并降低图像质量。

为了避免将它写入磁盘然后删除它,我使用了一个缓冲区,它工作正常。但现在我无法使用烧瓶 send_file 发送它。我尝试了一些解决方案,包括使用 werkzeug FileWrapper 包装它并使用 Response 发送,但它也不起作用。此外,它没有显示任何类型的错误...

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

当我指向系统中存在的文件时,它可以正常工作...

更新

有人指出我没有使用buffer.seek(0),戴上它后,我开始在我的请求中收到图像,但图像与我的预期相差甚远。

例如,我的测试图像是 5.2MB,当我将它保存到磁盘而不是缓冲区时,它会变成 250KB,但是当我尝试将它保存到缓冲区并使用 send_file 发送它时,它会变成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)

我正在编辑这个问题标题并删除flask的标签,因为我的问题似乎只是缺乏对io的BytesIO库的了解。

更新 2

当我想到它时,我正在从事另一个项目。如果我创建一个新的缓冲区来保存已经修改的图像怎么办?

它奏效了。

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

那么,显然我无法替换 BytesIO 缓冲区的内容?任何人都知道我做错了什么? (是的,我做到了,但我猜其他人会从同样的问题中受益?)

【问题讨论】:

  • @sahasrara62 我想我不明白。但是你链接显示了 seek(0) 的使用,我试过了,它确实返回了一个图像,但没有优化。很明显,我的缓冲区没有像我想象的那样工作。
  • 好吧,尝试使用不同的缓冲区/缓存系统来避免这种情况,在您的项目中创建一个服务/管道来获取图像并使用该缓存系统进行操作并返回像素化图像,这将是可由您的服务器提供服务。
  • image.save(buffer,...) 之后尝试buffer.truncate(),然后调用buffer.seek(0)send_file(buffer,...)
  • @JustinEzequiel 我也试过了(我只是重试,只是为了确定)来的图像不是保存的版本。

标签: python bytesio


【解决方案1】:

在我的机器上测试,save(...) 之后的truncate() 工作正常。

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)

【讨论】:

  • 天啊,我太笨了。我将 truncate() 放在保存之前,认为它会“擦除”缓冲区。好吧,它现在完美运行。非常感谢您,也很抱歉在您在评论中明确给出正确答案时浪费您的时间。
猜你喜欢
  • 2011-01-22
  • 1970-01-01
  • 2012-01-31
  • 1970-01-01
  • 2013-08-22
  • 2016-07-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多