Не удается сохранить изменения в буфере BytesIO

Пока я изучаю Flask, я написал небольшой сервис, который получает изображение, изменяет его размер и снижает его качество.

Чтобы не записывать его на диск, а потом удалять, я использую буфер, и это сработало нормально. Но теперь я не могу отправить его с помощью 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,2 МБ, когда я сохраняю его на диск вместо буфера, оно становится равным 250 КБ, но когда я пытаюсь сохранить его в буфер и отправить с помощью send_file, оно становится равным 5,5 МБ. ..

@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? Кто-нибудь знает, что я делаю неправильно? (да, я заставил это работать, но я думаю, что другим людям будет полезно решить ту же проблему?)

изменить изображение на BytesIO, stackoverflow.com/questions/35710361/…

sahasrara62 10.12.2020 22:14

@ sahasrara62 Кажется, я не понимаю. Но ваша ссылка показывает использование seek (0), я попробовал, и он вернул изображение, но не оптимизированное. Понятно, что мой буфер не работал, как я себе представлял.

Guilherme Richter 10.12.2020 22:39

ну, попробуйте использовать разные системы буфера/кэша, чтобы избежать этого, создайте сервис/конвейер в своем проекте, который берет изображение и использует эту систему кэширования для выполнения манипуляций и возврата пиксельного изображения, которое будет обслуживаться вашим сервером.

sahasrara62 11.12.2020 00:58

Попробуйте buffer.truncate() после image.save(buffer,...), затем позвоните buffer.seek(0) и send_file(buffer,...).

user5386938 11.12.2020 20:14

@JustinEzequiel Я тоже пробовал (я просто повторяю попытку, просто чтобы убедиться), что изображение не является сохраненной версией.

Guilherme Richter 11.12.2020 20:29

Включите весь ваш импорт, чтобы мы тоже могли протестировать.

user5386938 11.12.2020 20:32

@JustinEzequiel import io import sys import ведение журнала из PIL import Image from flask import Flask, send_file, request

Guilherme Richter 11.12.2020 21:10
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
7
645
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проверено на моей машине и truncate() после save(...) работает просто отлично.

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() ПЕРЕД сохранением, думая, что это «сотрет» буфер. Ну теперь работает отлично. Большое спасибо, и извините за потраченное время, когда вы четко даете мне правильный ответ в комментарии.

Guilherme Richter 11.12.2020 21:46

Другие вопросы по теме