Пока я изучаю 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? Кто-нибудь знает, что я делаю неправильно? (да, я заставил это работать, но я думаю, что другим людям будет полезно решить ту же проблему?)
@ sahasrara62 Кажется, я не понимаю. Но ваша ссылка показывает использование seek (0), я попробовал, и он вернул изображение, но не оптимизированное. Понятно, что мой буфер не работал, как я себе представлял.
ну, попробуйте использовать разные системы буфера/кэша, чтобы избежать этого, создайте сервис/конвейер в своем проекте, который берет изображение и использует эту систему кэширования для выполнения манипуляций и возврата пиксельного изображения, которое будет обслуживаться вашим сервером.
Попробуйте buffer.truncate()
после image.save(buffer,...)
, затем позвоните buffer.seek(0)
и send_file(buffer,...)
.
@JustinEzequiel Я тоже пробовал (я просто повторяю попытку, просто чтобы убедиться), что изображение не является сохраненной версией.
Включите весь ваш импорт, чтобы мы тоже могли протестировать.
@JustinEzequiel import io import sys import ведение журнала из PIL import Image from flask import Flask, send_file, request
Проверено на моей машине и 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() ПЕРЕД сохранением, думая, что это «сотрет» буфер. Ну теперь работает отлично. Большое спасибо, и извините за потраченное время, когда вы четко даете мне правильный ответ в комментарии.
изменить изображение на
BytesIO
, stackoverflow.com/questions/35710361/…