1. 程式人生 > >基於one2team框架的Highcharts圖表圖片導出方案

基於one2team框架的Highcharts圖表圖片導出方案

exception tchar 官方 過程 author 處理 phi base rtt

這篇文章已經沒有什麽意義了,新版的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圖表圖片導出方案