39

Sto usando il pallone per la mia applicazione. Mi piacerebbe inviare un'immagine (generata dinamicamente da PIL) al client senza salvare su disco.Come inviare un'immagine generata da PIL al browser?

Qualche idea su come procedere?

+1

Flask non sembra avere un solido supporto per lo streaming di dati binari che è possibile Generare con un generatore Python. Probabilmente dovrai memorizzare l'immagine in memoria e inviarla. – millimoose

risposta

16

In primo luogo, è possibile salvare l'immagine in un tempfile e rimuovere il file locale (se ne avete uno):

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) 

In secondo luogo, impostare il file temporaneo alla risposta (come da 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

Non funziona più: 'TypeError: 'file' oggetto non è richiamabile' – letmaik

+0

@neo - Grazie, aggiornato per send_file –

+0

Qual è il file creato dinamicamente qui? – user1953366

7

Ho anche faticato nella stessa situazione. Infine, ho trovato la sua soluzione usando un'applicazione WSGI, che è un oggetto accettabile per "make_response" come argomento.

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) 

Si prega di sostituire "l'apertura di immagine" operazioni con opportune operazioni di Pil.

+0

Questo funziona magnificamente :) – cybertoast

7

Si scopre che boccetta fornisce una soluzione (RTM a me!):

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

Ecco una versione senza alcun file temporanei e simili (vedi 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') 

Per utilizzare nel codice semplicemente

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

Questa è una risposta di gran lunga superiore alla risposta accettata. – jrs

+0

Significa che devi essere in grado di mantenere l'intera immagine in memoria in una sola volta, giusto? Potrebbe essere un problema con immagini enormi o altri tipi di download. – Eli

+0

@Eli per quei casi sembra più ragionevole usare un file temporaneo. Ad ogni modo, enormi file generati dinamicamente non sono comuni. – erickrf