2013-02-12 46 views
18

這個問題已被問到類似的方式here,但答案是我的頭(我是超級新的python和web開發),所以我希望有一個更簡單的方式,或者可以用不同的方式解釋。使用python動態提供matplotlib圖片到網頁

我試圖用matplotlib生成一個圖像,並在沒有先寫入文件到服務器的情況下提供它。我的代碼可能是一種愚蠢的,但它是這樣的:

import cgi 
import matplotlib.pyplot as pyplot 
import cStringIO #I think I will need this but not sure how to use 

...a bunch of matplotlib stuff happens.... 
pyplot.savefig('test.png') 

print "Content-type: text/html\n" 
print """<html><body> 
...a bunch of text and html here... 
<img src="test.png"></img> 
...more text and html... 
</body></html> 
""" 

我認爲與其做pyplot.savefig(「test.png」),我應該創建一個cstringIO對象,然後做一些事情像這樣:

mybuffer=cStringIO.StringIO() 
pyplot.savefig(mybuffer, format="png") 

但是我很失落。我見過的所有實例(例如http://lost-theory.org/python/dynamicimg.html)涉及做這樣的事情

print "Content-type: image/png\n" 

,我不明白如何集成了與我已經輸出HTML。

+0

當你輸出'imageout.py'團塊,然後在HTML可以將圖像源設置爲py腳本嗎?在PHP中,你會沿着這些線做一些事情。 – ninMonkey 2013-02-12 03:07:21

+1

退房http://stackoverflow.com/questions/1207190/embedding-base64-images – 2013-02-12 05:41:30

回答

18

你應該

  • 第一寫入到cStringIO對象
  • 然後寫入HTTP頭
  • 然後寫入cStringIO的內容到stdout

因此,如果出現savefig錯誤,您仍然可以返回其他內容,甚至另一個標題。一些錯誤將不會被早期識別,例如文本的某些問題,圖像尺寸過大等。

您需要告知savefig在哪裏寫輸出。你可以這樣做:

format = "png" 
sio = cStringIO.StringIO() 
pyplot.savefig(sio, format=format) 
print "Content-Type: image/%s\n" % format 
msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY) # Needed this on windows, IIS 
sys.stdout.write(sio.getvalue()) 

如果您想將圖像嵌入到HTML:

print "Content-Type: text/html\n" 
print """<html><body> 
...a bunch of text and html here... 
<img src="data:image/png;base64,%s"/> 
...more text and html... 
</body></html>""" % sio.getvalue().encode("base64").strip() 
+0

完美的作品,謝謝! – 2013-02-16 22:56:48

+0

不客氣... – 2013-02-16 23:55:24

2

除非我不好就將誤解你的問題,所有你需要做的是cd到圖像的位置和運行:蟒蛇-m SimpleHTTPServer 8000 &

然後打開瀏覽器,在地址欄中鍵入http://localhost:8000/

+1

不是我在找什麼,但你促使我爲了清晰起見而改名,謝謝! – 2013-02-12 03:01:51

5

我的第一個問題是:圖像是否經常變化?你想保留舊的?如果這是一個實時的事情,那麼你對優化的追求是合理的。否則,即時生成圖像的好處並不明顯。

的代碼,因爲它主張將需要2個請求:

  1. 得到的HTML源代碼你已經有了和
  2. ,以獲得實際圖像

也許最簡單的方式(保持網絡請求至少)是@AlexL的評論,它可以讓你在一個請求中完成它,通過構建一個嵌入了圖像的HTML。

您的代碼將是這樣的:

# Build your matplotlib image in a iostring here 
# ...... 
# 

# Initialise the base64 string 
# 
imgStr = "data:image/png;base64," 

imgStr += base64.b64encode(mybuffer) 

print "Content-type: text/html\n" 
print """<html><body> 
# ...a bunch of text and html here... 
    <img src="%s"></img> 
#...more text and html... 
    </body></html> 
""" % imgStr 

此代碼可能不會開箱的,但給出了這個概念。

請注意,如果您的圖片沒有真正更改太頻繁或生成時間過長,這通常是一個糟糕的主意,因爲每次都會生成

另一種方法是生成原始的html。加載它會觸發對「test.png」的請求。您可以通過您已經提到的緩衝流解決方案或通過靜態文件單獨提供。個人而言,我會堅持一個解耦的解決方案:通過另一個進程生成圖像(確保總是有可用的圖像),並使用非常輕的東西來生成並提供HTML。

HTH,

6

以上的答案是有點過時 - 這裏是什麼對我的作品在Python3 +獲得的原始字節圖形數據。

import matplotlib.pyplot as plt 
from io import BytesIO 
fig = plt.figure() 
plt.plot(range(10)) 
figdata = BytesIO() 
fig.savefig(figdata, format='png') 

正如在其他的答案中提到您現在需要設置一個「Content-Type的」頭「圖像/ PNG」,寫出來的字節。

根據您用作網絡服務器的代碼可能會有所不同。我用旋風作爲我的網絡服務器和代碼要做到這一點是:

self.set_header('Content-Type', 'image/png') 
self.write(figdata.getvalue()) 
+0

我無法獲得正確的緩衝區內容長度。當我通過ajax下載圖片時,我總是遇到'ERR_CONTENT_LENGTH_MISMATCH'錯誤,嘗試了'sys.getsizeof(figdata)'和'figdata .__ sizeof __()'。我最終沒有指定內容長度。 – TomTom101 2017-01-08 21:47:06

+1

@ TomTom101 - 你使用什麼庫爲你的HTTP服務器?你可能需要'len(figdata.getvalue())'(你得到的是BytesIO對象的大小,而不是實際的數據流) – 2017-01-09 08:18:38

+0

使用nginx,Gunicorn和Falcon。我會嘗試你的建議 – TomTom101 2017-01-09 08:24:56

1

什麼用python3對我的作品是:

buf = io.BytesIO() 
plt.savefig(buf, format='png') 
image_base64 = base64.b64encode(buf.getvalue()).decode('utf-8').replace('\n', '') 
buf.close() 
相關問題