基於one2team框架的Highcharts圖表圖片導出方案
這篇文章已經沒有什麽意義了,新版的HIghcharts提供Java圖片導出解決方案,你需要做的就是下個Maven,bulid一個war就Ok了。---addedy on 2012-11-15
多說一句廢話:我覺得這個功能其實對於大多數應用場景來說是多余的。
Highcharts是一個純JSWeb圖表繪制解決方案,它的功能之豐富,使用之簡單可能是目前開源領域排名比較考前的優秀解決方案,它對個人使用是免費的。
它的默認版本也有圖片導出功能,不過導出服務器是Highcharts官方服務器,我開發的過程試了一下,好像特別慢,圖片導出服務用自己的對商業用戶來說也有“便利之處”。
官方的下載頁面推薦的Java圖片導出方案是one2team/highcharts-serverside-export,這個方案是基於apache的batik包的,也有人直接采用batik包開發了圖片導出Servlet代碼 (這個代碼沒好像沒有解決中文問題)。我是比較軸的那種人,本來one2team和上面這個代碼核心是一樣,我還是研究了一下怎麽使用這個官方推薦的導出框架。說實話,官方推薦的這個框架不是很好用,它采用JDK6的泛型特性,項目的編譯器兼容性必須提高到1.6,否則編譯會出錯。其次這個框架的主HighchartsExporter類的功能是轉換以Json數據或者Java語言對象為數據源的導出功能,而Highchart導出服務器是要轉換Highchart圖表post的SVG數據。所以需要對One2Team的框架稍加改造才能使用。One2team的框架的OO設計比較復雜,我就不畫具體的UML圖了。只列出需要引進的幾個類:
首先繼承SVGRendererInternal抽象類,重寫callJavascript方法,這個方法的實際作用是把其他格式的數據源轉換成SVG數據,我不是很明白為什麽SVG數據生成非要在Java裏面調用JavaScript來做,難道為了和瀏覽器的高度一致?,這個地方我們直接返回chartoption即可,這個chartoption在SVG源數據導出情況下就是SVG數據本書,所以無需處理,直接返回即可。
/** * */ package org.one2team.highcharts.server.export.util; import org.mozilla.javascript.ScriptableObject;import org.one2team.highcharts.server.export.util.SVGRendererInternal; import org.one2team.highcharts.shared.ChartOptions; /** * @author Dipolar * */ public class SVGRendererInternalSVG extends SVGRendererInternal<String> { @Override protected Object callJavascript (final String generalOptions, final String chartOptions) { //return ScriptableObject.callMethod (null, SCRIPTABLE, "renderSVGFromObject", new Object [] {‘(‘ + generalOptions + ‘)‘, chartOptions}); return chartOptions; } }
本來加這麽一個類,導出圖片就夠了,我為了方便Servlet操作,我又模仿HighchartsExporter類重寫了一個HttpHighchartsExporter類,代碼如下:上面這兩個類都放在了one2team的包類,因為涉及到的一些類是protected的,為了不改寫人家的代碼,所以只好放在人家的包裏了。還有代碼中的globalOptions參數可以省略不計,直接賦null即可,因為這個參數在實際轉換中並沒有用,實際轉換中的globalOptions用的是一個框架本身預定義的常量。
package org.one2team.highcharts.server.export; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.OutputStream; import org.apache.commons.io.IOUtils; import org.one2team.highcharts.server.export.ExportType; import org.one2team.highcharts.server.export.util.*; public class HttpHighchartsExporter<T> { public HttpHighchartsExporter(ExportType type, SVGRendererInternal<T> internalRenderer) { this.type = type; this.renderer = new SVGStreamRenderer<T> (new SVGRenderer<T> (internalRenderer), type.getTranscoder ()); } public HttpHighchartsExporter(ExportType type) { SVGRendererInternal svgRender=new SVGRendererInternalSVG(); this.type = type; this.renderer = new SVGStreamRenderer<T> (new SVGRenderer<T> (svgRender), type.getTranscoder ()); } public void export (T chartOptions, T globalOptions, OutputStream out) { OutputStream fos = null; try { fos = render (chartOptions, globalOptions, out); } catch (Exception e) { e.printStackTrace (); throw (new RuntimeException (e)); } finally { if (fos != null) IOUtils.closeQuietly (fos); } } private OutputStream render (T chartOptions, T globalOptions, OutputStream out) throws FileNotFoundException { renderer.setChartOptions (chartOptions) .setGlobalOptions (globalOptions) .setOutputStream (out) .render (); return out; } public SVGStreamRenderer<T> getRenderer () { return renderer; } public ExportType getType () { return type; } private final SVGStreamRenderer<T> renderer; private final ExportType type; }
下面來一下真正的Servlet的 doPost的代碼,這個代碼雖然解決了jpg和png的中文亂碼問題,但是沒有解決SVG的導出的中文亂碼問題,SVG導出采用svg2svgTranscoder的話,在框架內部的字體調用上出bug了,直接輸出svg,不加OutputStreamWriter包裝的情況下,chrome可以正常打開這個xml文件,但是中文是亂碼),IE8下在中文位置出問題,加了OutputStreamWriter包裝器以後,Chrome下圖片可以正常渲染但是報某元素屬性值有問題,IE下報了另外一個錯誤。PDF的導出需要擴充下一下ExportType枚舉,PDFTranscoder類也不在batik的包裏在apache的fop項目裏面,我嘗試了一下報出Java堆空間不夠的錯誤,我的生產服務器資源也有限,PDF導出功能就省略把。
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try{ request.setCharacterEncoding("utf-8");//這一行解決了PNG、JPG的中文亂碼問題。 String filename=request.getParameter("filename"); String type=request.getParameter("type"); type=type.replace("svg+", ""); MimeType mtype=new MimeType(type); response.addHeader("Content-Disposition", "attachment; filename="+ filename + "."+mtype.getSubType()); response.addHeader("Content-Type", mtype.getBaseType()+"; charset=UTF-8"); ServletOutputStream out=response.getOutputStream(); String svg=request.getParameter("svg"); if (mtype.getSubType().equals("xml")){ //OutputStreamWriter writer = new OutputStreamWriter(out, "utf-8"); //writer.write(svg); }else{ HttpHighchartsExporter<String> httpExporter = new HttpHighchartsExporter<String> (Enum.valueOf(ExportType.class,mtype.getSubType())); httpExporter.export(svg,null,out); } out.flush(); out.close(); }catch(Exception e){ e.printStackTrace(); } }
結語:最後提示一下one2team雖然在做圖片導出並不一定好,但是JSM系列類可以用來很方便地為前端生成圖表繪制所需要的Json數據,當然這個Json數據可以你自己硬代碼生成Json數據,但是既然有了與HighChart對象一一對象的Java類,為什麽不用一下哪?
基於one2team框架的Highcharts圖表圖片導出方案