39

Estoy usando matraz para mi aplicación. Me gustaría enviar una imagen (generada dinámicamente por PIL) al cliente sin guardar en el disco.¿Cómo enviar imágenes generadas por PIL al navegador?

¿Alguna idea de cómo hacer esto?

+1

Flask no parece tener un soporte sólido para la transmisión de datos binarios que pueda Generar con un generador de Python. Probablemente tendrá que almacenar la imagen en la memoria y enviarla. – millimoose

Respuesta

16

En primer lugar, se puede guardar la imagen en un tempfile y eliminar el archivo local (si lo tiene):

from tempfile import NamedTemporaryFile 
from shutil import copyfileobj 
from os import remove 

tempFileObj = NamedTemporaryFile(mode='w+b',suffix='jpg') 
pilImage = open('/tmp/myfile.jpg','rb') 
copyfileobj(pilImage,tempFileObj) 
pilImage.close() 
remove('/tmp/myfile.jpg') 
tempFileObj.seek(0,0) 

En segundo lugar, establecer el archivo temporal de la respuesta (según this stackoverflow question):

from flask import send_file 

@app.route('/path') 
def view_method(): 
    response = send_file(tempFileObj, as_attachment=True, attachment_filename='myfile.jpg') 
    return response 
+0

Ya no funciona: 'TypeError: 'file' object no se puede llamar' – letmaik

+0

@neo - Gracias, actualizado a send_file –

+0

¿Cuál es el archivo creado dinámicamente aquí? – user1953366

7

También estaba luchando en la misma situación. Finalmente, encontré su solución usando una aplicación WSGI, que es un objeto aceptable para "make_response" como argumento.

from Flask import make_response 

@app.route('/some/url/to/photo') 
def local_photo(): 
    print('executing local_photo...') 
    with open('test.jpg', 'rb') as image_file: 
     def wsgi_app(environ, start_response): 
      start_response('200 OK', [('Content-type', 'image/jpeg')]) 
      return image_file.read() 
     return make_response(wsgi_app) 

Reemplace las operaciones de "apertura de la imagen" con las operaciones de PIL apropiadas.

+0

Esto funciona maravillosamente :) – cybertoast

7

Resulta que frasco proporciona una solución (RTM a mí mismo!):

from flask import abort, send_file 
try: 
    return send_file(image_file) 
except: 
    abort(404) 
123

Aquí hay una versión sin ningún tipo de archivos temporales y similares (ver here):

def serve_pil_image(pil_img): 
    img_io = StringIO() 
    pil_img.save(img_io, 'JPEG', quality=70) 
    img_io.seek(0) 
    return send_file(img_io, mimetype='image/jpeg') 

Para usar en su código simplemente haga

@app.route('some/route/') 
def serve_img(): 
    img = Image.new('RGB', ...) 
    return serve_pil_image(img) 
+9

Esta es una respuesta muy superior a la respuesta aceptada. – jrs

+0

Significa que tienes que ser capaz de mantener toda la imagen en la memoria de una vez, ¿verdad? Podría ser un problema con imágenes enormes u otros tipos de descargas. – Eli

+0

@Eli para esos casos, parece más sensato usar un archivo temporal. De todos modos, los enormes archivos generados dinámicamente no son comunes. – erickrf

Cuestiones relacionadas