Seite 1 von 1
Bottle: HTML <img src...> aus String
Verfasst: Samstag 9. Oktober 2010, 20:43
von noisefloor
Hallo,
ich generiere mit ReportLab einen Graphen und würde diesen gerne (mittels Bottle) auf einer Webseite ausgeben. Den Graphen möchte ich aber nicht auf der HD speichern, sondern in einen String bzw. cStringIO-Objekt schreiben. Das Problem ist, dass HTML `<img src="...">` eine Datei erwartet...
Der Code, der
nicht funktioniert, sieht so aus:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*
import bottle
import cStringIO
from PIL import Image as PILI
from reportlab.graphics.shapes import *
from reportlab.graphics import renderPM
from reportlab.graphics.charts.piecharts import Pie
@bottle.route('/test')
def test():
graph = build_graph()
return bottle.template('html_test.tpl',graph = graph)
def build_graph():
buf = cStringIO.StringIO()
d = Drawing(200,100)
pc = Pie()
pc.x = 65
pc.y = 15
pc.width = 70
pc.height = 70
pc.data = [10,20,30,40]
pc.labels = ['a','b','c','d']
d.add(pc)
renderPM.drawToFile(d,buf,'PNG')
buf.reset()
im = PILI.open(buf)
im.load()
return im
bottle.debug(True)
bottle.run(reloader=True)
und das Template:
Code: Alles auswählen
<h1>Graph Test</h1>
<p>Text Text Text 1</p>
<img src="{{graph}}" alt="Ein Graph">
<p>Text Text Text 2</p>
Ich bin mir noch nicht mal sicher, ob und wie man dafür PIL braucht... Der oben gezeigte Code war der letzte Versuch...
Gruß, noisefloor
Re: HTML <img src...> aus String
Verfasst: Samstag 9. Oktober 2010, 20:49
von snafu
Trage in das HTML doch einfach einen Pseudo-Dateipfad ein. Diesen kannst du dann mit Bottle als Route abfangen und dort die Datei mittels `static_file()` (oder so ähnlich) zurückgeben. Man müsste nur wissen, ob die Funktion auch mit dateiartigen Objekten umgehen kann oder wirklich eine echte Datei erwartet. Alternativ könnte man natürlich noch kurzzeitig eine temporäre Datei anlegen und diese dann zurückgeben. Das ist aber sicher nicht so schön.
Übrigens noch eine andere Sache am Rande, da ich bei dem Thema daran gedacht habe, das früher oder später Caching relevant werden könnte: Wie ich heute entdeckt habe, wird das Entwicklung befindliche Python 3.2 einen Dekorator haben, der Funktionsrückgabewerte cachen kann:
@functools.lru_cache. Das könnte bestimmt gerade für Anwendungen wie Bottle oder Flask interessant werden.
Re: HTML <img src...> aus String
Verfasst: Samstag 9. Oktober 2010, 20:55
von noisefloor
Hallo,
in der "echten" Version kann eine HTML-Seite so bis zu 10 Grafiken / Graphen enthalten und es gibt ~10 Seiten. Von daher wäre der Weg via eigene Route etwas mühselig.
Gruß, noisefloor
Re: HTML <img src...> aus String
Verfasst: Samstag 9. Oktober 2010, 20:58
von Hyperion
noisefloor hat geschrieben:Hallo,
in der "echten" Version kann eine HTML-Seite so bis zu 10 Grafiken / Graphen enthalten und es gibt ~10 Seiten. Von daher wäre der Weg via eigene Route etwas mühselig.
Gruß, noisefloor
Wieso das? Es reicht doch eine Route, in der Du einen Schlüssel übergibst, anhand dessen Du dann den Graphen ausliefern kannst.
Re: HTML <img src...> aus String
Verfasst: Samstag 9. Oktober 2010, 21:09
von snafu
Du sollst ja nicht unbedingt für jede Route händisch eine eigene Funktion anlegen. Auch Bilddateien können zusätzlich Argumente für einen Query-String haben. Dort würden dann meinetwegen die Koordinaten drin sein oder was auch immer du genau vor hast. Siehe dazu das Bottle-Tutorial im Abschnitt
Accessing Request Data entsprechend der Unterpunkt "Query Strings".
Übrigens finde ich diesen Weg schon relativ elegant und es ist auch der einzig sinnvolle - wenn nicht gar: mögliche - Weg, der mir dafür einfällt.
Re: HTML <img src...> aus String
Verfasst: Samstag 9. Oktober 2010, 21:26
von snafu
Gerade gesehen, dass `static_file()` wohl zwingend einen Dateipfad erwartet und in der API für die neue Version von Bottle seh ich's igendwie gar nicht mehr. Vermutlich ist es dann am besten, wenn die Funktion einfach den Inhalt von `StringIO()` zurückgibt. Eventuell musst du da noch etwas tricksen, damit die Browser es korrekt als Grafik interpretieren. Aber vielleicht wissen auch Defnull oder jemand anders eine gute Lösung dafür.
Re: HTML <img src...> aus String
Verfasst: Sonntag 10. Oktober 2010, 07:32
von noisefloor
Hallo,
ok... werde mal beide Varianten probieren (also Route und Temporäre Datei).
Werde es wahrscheinlich erstmal mit cStringIO probieren und den Content händisch als 'PNG' deklarieren. Das sollte funktionieren, zumindest funktioniert das Äquivalent mit PDFs.
Dann noch eine Frage zu temporären Dateien: Wenn ich diesen Weg wähle -> wann werden temporäre Dateien (unter Linux) automatisch gelöscht? Nach einer Zeit X oder nur beim Reboot?
Gruß, noisefloor
Re: HTML <img src...> aus String
Verfasst: Sonntag 10. Oktober 2010, 10:00
von BlackJack
@noisefloor: Temporäre Dateien werden gar nicht automatisch gelöscht.
Re: HTML <img src...> aus String
Verfasst: Sonntag 10. Oktober 2010, 20:50
von noisefloor
Hallo,
also:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*
import bottle
import cStringIO
from reportlab.graphics.shapes import *
from reportlab.graphics import renderPDF, renderPM
from reportlab.graphics.charts.piecharts import Pie
@bottle.route('/test')
def test():
return bottle.template('html_test.tpl')
@bottle.route('/graph')
def build_graph():
buf = cStringIO.StringIO()
d = Drawing(200,100)
pc = Pie()
pc.x = 65
pc.y = 15
pc.width = 70
pc.height = 70
pc.data = [10,20,30,40]
pc.labels = ['a','b','c','d']
d.add(pc)
renderPM.drawToFile(d,buf,'PNG')
buf.reset()
bottle.response.headers['Content-Type'] = 'image/png'
return buf.read()
bottle.debug(True)
bottle.run(reloader=True)
liefert auf der Route '/graph' brav die Grafik zurück, aber bei
Code: Alles auswählen
<h1>Graph Test</h1>
<p>Text Text Text 1</p>
<img scr="http://localhost:8080/graph" alt="Ein Diagramm">
<p>Text Text Text 2</p>
passiert nix bzw. es wird das 'alt'-Attribut ausgegeben.
Wie snafu richtig sagt verlangt 'bottle.static_file()' eine reale Datei.
Bei Verwendung von temporären Dateien hätte ich halt das Problem, das mein /temp Verzeichnis wächst, weil die temporären Dateien ja nicht löschen kann, bevor Sie mit dem Template returniert werden...
Gruß, noisefloor
Re: HTML <img src...> aus String
Verfasst: Montag 11. Oktober 2010, 06:50
von Darii
noisefloor hat geschrieben:Das Problem ist, dass HTML `<img src="...">` eine Datei erwartet...
Nein, nicht zwingend. Fast alle Browser (selbst IE) beherrschen inzwischen das
data-URL-Schema. Du kannst das Bild also einfach base64-kodiert in die Datei schreiben.
Re: HTML <img src...> aus String
Verfasst: Montag 11. Oktober 2010, 07:58
von noisefloor
Hallo,
@Darii: ah, interessant. Probiere ich heute abend mal aus.
Gruß, noisefloor
Re: HTML <img src...> aus String
Verfasst: Montag 11. Oktober 2010, 09:46
von snafu
Code: Alles auswählen
from base64 import b64encode
from mimetypes import guess_type
def get_data_uri(fileobj, mime_type=None):
if not mime_type and hasattr(fileobj, 'name'):
mime_type = guess_type(fileobj.name)[0]
if not mime_type:
# should of course use a special exception type
raise Exception, 'Could not guess missing MIME-type'
data = b64encode(fileobj.read())
return 'data:{0};base64,{1}'.format(mime_type, data)
Re: HTML <img src...> aus String
Verfasst: Montag 11. Oktober 2010, 19:55
von noisefloor
Hallo,
grundsätzlich sollte folgender Code funktionieren:
Code: Alles auswählen
#!/usr/bin/env python
# -*- coding: utf-8 -*
import bottle
import cStringIO
from mimetypes import guess_type
from base64 import b64encode
from reportlab.graphics.shapes import *
from reportlab.graphics import renderPDF, renderPM
from reportlab.graphics.charts.piecharts import Pie
@bottle.route('/test')
def test():
graph = build_graph()
return bottle.template('html_test.tpl',graph=graph)
def build_graph():
buf = cStringIO.StringIO()
d = Drawing(200,100)
pc = Pie()
pc.x = 65
pc.y = 15
pc.width = 70
pc.height = 70
pc.data = [10,20,30,40]
pc.labels = ['a','b','c','d']
d.add(pc)
renderPM.drawToFile(d,buf,'PNG')
buf.reset()
graph_uri = get_data_uri(buf,mime_type='png')
return graph_uri
def get_data_uri(fileobj, mime_type=None):
if not mime_type and hasattr(fileobj, 'name'):
mime_type = guess_type(fileobj.name)[0]
if not mime_type:
# should of course use a special exception type
raise Exception, 'Could not guess missing MIME-type'
data = b64encode(fileobj.read())
return 'data:image/{0};base64,{1}'.format(mime_type, data)
bottle.debug(True)
bottle.run(reloader=True)
Code: Alles auswählen
<h1>Graph Test</h1>
<p>Text Text Text 1</p>
<img scr="{{!graph}}" alt="Ein Diagramm" />
<p>Text Text Text 2</p>
ABER: Bottle funkt irgendwie dazwischen... Beim `{{!graph}}` zerlegt Bottle den einzeiligen String scheinbar in mehrere Zeilen, wodurch der Browser die URI nicht mehr korrekt interpretiert. Fügt man das Ergebnis von `'data:image/{0};base64,{1}'.format(mime_type, data)` händisch als HTML-Code ein, zeigt der Browser das Diagramm an.
Ergo: Ein Bottle-Problem ?!
BTW: kann ein Moderator den Thread mal ins Webframework-Forum schieben? Danke.
Gruß, noisefloor