一直在使用Highcharts做web圖表的展示, 但是當發送定時的報表郵件的遇到了這個問題. 為了保證郵件圖表和web頁圖表樣式一致, 需要能原樣生圖. 這里考慮如下:
1. 如何生成jpg圖片
在很久以前,官方推薦用Imagemagick將SVG轉成jpg. 考慮如何得到SVG, 大量文檔推薦用 nodejs+jsdom從json數據中生成svg. 在實際使用中, Imagemagick和jsdom環節處理復雜的數據都非常孱弱, 復雜的圖表丟東少西難看要死, 無法上線. 在google上糾結好久, 終於發現了這神器PhantomJS.
做測試這么久, 盡然不知道PhantomJS這種神器. 簡單說, PhantomJS是個不顯示UI的瀏覽器, 可以無GUI的server上使用. 而且核心為webkit, 輕松支持Highcharts等幾乎所有的js庫. 在本文中, PhantomJS拿來解決這個問題再合適不過. 只需要想辦法把數據傳給PhantomJS, 簡單幾句代碼, 截個圖就行了.
2. 數據傳遞
下面這個問題就是怎么把數據傳給PhantomJS. PhantomJS支持命令行, 在服務器端, (一般為Java, Python, PHP), 與JS交互的最好數據格式就是JSON. 但是JSON數據比較大時, 直接用命令行就不行了. 傳遞方式可以選:
- fork子進程, 利用其stdin吐進去
- 生成一個json臨時文件, 命令行指定該文件地址
- 利用PhantomJS的webserver模塊單獨開啟一個服務, 向其POST
三種方式都可以, 看具體的需求. 第二種比較簡單, 就是總要生成中間文件, 不是很喜歡....
還有一個問題, 利用JSON提供基本數據還可以, 但是傳遞控制顯示樣式的callback函數(比如formatter)比較麻煩. 如果所有圖表樣式統一還好說, 在PhantomJS腳本內寫死就可以. 對於圖表需要個性樣式的情況, 目前想到的解決辦法是, 將callback寫成各個js文件, 在傳遞JSON時指定一個額外的配置, 確定讀取哪個js文件, 然后再統一到一起.
這兩個問題解決, 就可以直接從服務器端生成圖片了.
補充: 剛看到了官方的解決方案, 跟我想法差不多, 地址如下:
http://www.highcharts.com/component/content/article/2-news/52-serverside-generated-charts
而且在文章后半部, 指出了PhantomJS在數據點過多情況下的性能問題.
稍有不同的是, 官方使用了命令行參數指定callback js文件, 並且callback是通過Highcharts強大的api來調整樣式的. 而不是向本文一樣做合並.