最近项目中报表数据的图表展示采用了FusionCharts,功能需求如下:
1.提供在线flash图表展示。
2.根据周期性报表的数据,生成静态html文件,提供用户订阅。
功能实现思路:
1.对于提供在线flash图表展示,主要参考了FusionCharts的文档示例,具体实现可见下面实例。注意一点:
因为生成xml文件会增加磁盘IO操作,会降低图表生成功能的整体性能,所以对于用户请求返回页面呈现chart的需求,采取不生成xml数据文件的方式。在default.jsp中包含PieData.jsp,由PieData.jsp生成数据流,在default.jsp中输出渲染为swf图表。
2.根据需求从DB中获取所需数据。
2.1生成data.xml数据文件.
2.2生成html文件,组装显示所需的文件,例如:FusionCharts.js,对应的swf等。
2.3生成需求规定的文件目录结构,将html及相关文件分别放到指定的位置。
由于FusionCharts的示例中太多的逻辑采用了jsp来获取或者生成,所以对它进行了简单的封装,目的是方便项目组其他人对FusionCharts的使用。
效果图:
示例:
1.FusionCharts图表展示
采用了jsp中生成data.xml的方式。
default.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@page import="org.apache.commons.logging.Log"%> <%@page import="org.apache.commons.logging.LogFactory"%> <%@page import="com.test.FusionChartUtil"%> <%@page import="com.test.FusionChartsDTO"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <%@ include file = "../Includes/FusionCharts.jsp" %> <HTML> <HEAD> <TITLE>3D饼图</TITLE> <link rel="stylesheet" type="text/css" href="<%=path%>/css/st/css.css" /> <link rel="stylesheet" type="text/css" href="<%=path%>/css/st/home.css" /> <SCRIPT LANGUAGE="Javascript" SRC="<%=basePath%>FusionCharts/FusionCharts.js"></SCRIPT> <style type="text/css"> <!-- body { font-family: Arial, Helvetica, sans-serif; font-size: 12px; } .text{ font-family: Arial, Helvetica, sans-serif; font-size: 12px; } --> </style> </HEAD> <BODY> <CENTER> <h2>查询表TAB_OMIN_ST_G_SDATAAREA_H</h2> <% Log logger = LogFactory.getLog(getClass()); String animate = (String)request.getAttribute("animate"); String dataPath = path +"/jsp/st/bs/DB_dataURL/PieData.jsp?animate="+ animate; String swfPath = path +"/FusionCharts/Pie3D.swf"; logger.info("^^^^^^^^^^^^^^^^^^^^^^^^^ dataPath = "+ dataPath); FusionChartsDTO fusionCharts = new FusionChartsDTO(); fusionCharts.setStrDataURL(dataPath); fusionCharts.setAddNoCacheStr("false"); fusionCharts.setResponse(response); fusionCharts.setSwfURL(swfPath); fusionCharts.setDataURL("dataPath"); fusionCharts.setChartId("FactorySum"); fusionCharts.setChartWidth(600); fusionCharts.setChartHeight(300); fusionCharts.setDebugMode(false); fusionCharts.setRegisterWithJS(false); String chartCode = FusionChartUtil.genChart(fusionCharts); out.print(chartCode); out.print("<p><p>"); out.print((String)request.getAttribute("tableData")); %> <BR> <BR> </CENTER> </BODY> </HTML>
PieData.jsp
参考FusionCharts的jsp示例
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; %> <%@ page import="java.io.*"%> <%@ page import="com.test.PieChartXmlDataProvider"%> <% String strXML=""; //Default.jsp has passed us a property animate. We request that. String animateChart; animateChart = request.getParameter("animate"); //Set default value of 1 if(null==animateChart||animateChart.equals("")){ animateChart = "1"; } String strQuery = "select I_CITY_NAME,sum(I_FACTSTA_TIMES) as FACTSTA_TIMES from tab_omin_st_g_sdataarea_h group by I_CITY_NAME"; strXML = new PieChartXmlDataProvider().getXmlData(animateChart,strQuery,"dataURL"); //logger.info("^^^^^^^^^^^^^^^^^^^^^^^^^ pieData strXML = "+ strXML); //Set Proper output content-type //解决中文乱码问题 response.setContentType( "text/xml; charset=UTF-8" ); OutputStream outs = response.getOutputStream(); outs.write( new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF} ); outs.write(strXML.getBytes("UTF-8")); outs.flush(); outs.close(); outs = null; response.flushBuffer(); out.clear(); out = pageContext.pushBody(); %>
PieChartXmlDataProvider.java
数据查询类,返回组装数据的<chart/>标签
public class PieChartXmlDataProvider extends AbsChartXmlDataProvider{ private static Logger logger = Logger.getLogger(PieChartXmlDataProvider.class); public String getXmlData(String animateChart,String sql,String getDataType){ String cityName = ""; String rightRate = ""; StringBuffer dataBuffer = new StringBuffer(""); dataBuffer.append("<chart caption='example' subCaption='Sum I_RIGHT_RATE by city' pieSliceDepth='30' " + "showBorder='1' formatNumberScale='0' numberSuffix=' Units'"); if(getDataType.equals(BsConstants.DATA_TYPE_DATA_URL)){ dataBuffer.append("animation=' " + animateChart + "'>"); }else if(getDataType.equals(BsConstants.DATA_TYPE_DATA_XML)){ dataBuffer.append(">"); } try{ DAO dao = BF.getDAO(); List<Map> rsList = dao.executeQuery(sql,"查询数据"); for (Map map : rsList) { cityName = (String)map.get("I_CITY_NAME"); rightRate = ((BigDecimal)map.get("FACTSTA_TIMES")).toString(); //logger.info("^^^^^^^^^^^^^^^^^^^^^^^^^ cityName = "+ cityName); //logger.info("^^^^^^^^^^^^^^^^^^^^^^^^^ rightRate = "+ rightRate); dataBuffer.append("<set label='" + cityName + "' value='" +rightRate+ "' />"); } dataBuffer.append("</chart>"); logger.info("^^^^^^^^^^^^^^^^^^^^^^^^^ pieData strXML = "+ dataBuffer.toString()); }catch (Exception e) { e.printStackTrace(); } return dataBuffer.toString(); } }
FusionChartUtil.java
生成html文件util类
import javax.servlet.http.HttpServletResponse; public class FusionChartUtil { /** * 生成FusionCharts图 * @param fusionCharts * @return */ public static String genChart(FusionChartsDTO fusionCharts) { String strDataURL = fusionCharts.getStrDataURL() == null ? "" : fusionCharts.getStrDataURL(); String addNoCacheStr = fusionCharts.getAddNoCacheStr() == null ? "" : fusionCharts.getAddNoCacheStr(); String swfPath = fusionCharts.getSwfURL() == null ? "" : fusionCharts.getSwfURL(); String strXML = fusionCharts.getDataXML() == null ? "" : fusionCharts.getDataXML(); String chartId = fusionCharts.getChartId() == null ? "" : fusionCharts.getChartId(); int chartWidth = fusionCharts.getChartWidth() <= 0 ? 600 : fusionCharts.getChartWidth(); int chartHeight = fusionCharts.getChartHeight() <= 0 ? 300 : fusionCharts.getChartHeight(); String encodeDataURL = encodeDataURL(strDataURL,addNoCacheStr,fusionCharts.getResponse()); return createChart(swfPath,encodeDataURL,strXML,chartId,chartWidth,chartHeight, fusionCharts.isDebugMode(),fusionCharts.isRegisterWithJS()); } /** * Encodes the dataURL before it's served to FusionCharts. * If you have parameters in your dataURL, you necessarily need to encode it. * @param strDataURL - dataURL to be fed to chart * @param addNoCacheStr - Whether to add aditional string to URL to disable caching of data * @return */ public static String encodeDataURL(String strDataURL, String addNoCacheStr, HttpServletResponse response) { String encodedURL = strDataURL; //Add the no-cache string if required if (addNoCacheStr.equals("true")) { /*We add ?FCCurrTime=xxyyzz If the dataURL already contains a ?, we add &FCCurrTime=xxyyzz We send the date separated with '_', instead of the usual ':' as FusionCharts cannot handle : in URLs */ java.util.Calendar nowCal = java.util.Calendar.getInstance(); java.util.Date now = nowCal.getTime(); java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat( "MM/dd/yyyy HH_mm_ss a"); String strNow = sdf.format(now); if (strDataURL.indexOf("?") > 0) { encodedURL = strDataURL + "&FCCurrTime=" + strNow; } else { strDataURL = strDataURL + "?FCCurrTime=" + strNow; } encodedURL = response.encodeURL(strDataURL); } return encodedURL; } /** * Creates the Chart HTML+Javascript to create the FusionCharts object with the given parameters. * This method uses JavaScript to overcome the IE browser problem with SWF wherein you have to 'Click to activate' the control * @param chartSWF - SWF File Name (and Path) of the chart which you intend to plot * @param strURL - If you intend to use dataURL method for this chart, pass the URL as this parameter. Else, set it to "" (in case of dataXML method) * @param strXML - If you intend to use dataXML method for this chart, pass the XML data as this parameter. Else, set it to "" (in case of dataURL method) * @param chartId - Id for the chart, using which it will be recognized in the HTML page. Each chart on the page needs to have a unique Id. * @param chartWidth - Intended width for the chart (in pixels) * @param chartHeight - Intended height for the chart (in pixels) * @param debugMode - Whether to start the chart in debug mode * @param registerWithJS - Whether to ask chart to register itself with JavaScript */ public static String createChart(String chartSWF, String strURL, String strXML, String chartId, int chartWidth, int chartHeight, boolean debugMode, boolean registerWithJS) { StringBuffer strBuf = new StringBuffer(); /* First we create a new DIV for each chart. We specify the name of DIV as "chartId"Div. DIV names are case-sensitive. */ strBuf.append("<!--START Script Block for Chart -->\n"); strBuf.append("\t\t<div id='" + chartId + "Div' align='center'>\n"); strBuf.append("\t\t\t\tChart.\n"); /*The above text "Chart" is shown to users before the chart has started loading (if there is a lag in relaying SWF from server). This text is also shown to users who do not have Flash Player installed. You can configure it as per your needs.*/ strBuf.append("\t\t</div>\n"); /*Now, we render the chart using FusionCharts Class. Each chart's instance (JavaScript) Id is named as chart_"chartId".*/ strBuf.append("\t\t<script type='text/javascript'>\n"); //Instantiate the Chart Boolean registerWithJSBool = new Boolean(registerWithJS); Boolean debugModeBool = new Boolean(debugMode); int regWithJSInt = boolToNum(registerWithJSBool); int debugModeInt = boolToNum(debugModeBool); strBuf.append("\t\t\t\tvar chart_" + chartId + " = new FusionCharts('" + chartSWF + "', '" + chartId + "', '" + chartWidth + "', '" + chartHeight + "', '" + debugModeInt + "', '" + regWithJSInt + "');\n"); //Check whether we've to provide data using dataXML method or dataURL method if (strXML.equals("")) { strBuf.append("\t\t\t\t//Set the dataURL of the chart\n"); strBuf.append("\t\t\t\tchart_" + chartId + ".setDataURL(\"" + strURL + "\");\n"); } else { strBuf.append("\t\t\t\t//Provide entire XML data using dataXML method\n"); strBuf.append("\t\t\t\tchart_" + chartId + ".setDataXML(\"" + strXML + "\");\n"); } strBuf.append("\t\t\t\t//Finally, render the chart.\n"); strBuf.append("\t\t\t\tchart_" + chartId + ".render(\"" + chartId + "Div\");\n"); strBuf.append("\t\t</script>\n"); strBuf.append("\t\t<!--END Script Block for Chart-->\n"); return strBuf.substring(0); } /** * Creates the Chart HTML to embed the swf object with the given parameters * @param chartSWF - SWF File Name (and Path) of the chart which you intend to plot * @param strURL - If you intend to use dataURL method for this chart, pass the URL as this parameter. Else, set it to "" (in case of dataXML method) * @param strXML - If you intend to use dataXML method for this chart, pass the XML data as this parameter. Else, set it to "" (in case of dataURL method) * @param chartId - Id for the chart, using which it will be recognized in the HTML page. Each chart on the page needs to have a unique Id. * @param chartWidth - Intended width for the chart (in pixels) * @param chartHeight - Intended height for the chart (in pixels) * @param debugMode - Whether to start the chart in debug mode */ public String createChartHTML(String chartSWF, String strURL, String strXML, String chartId, int chartWidth, int chartHeight, boolean debugMode) { /*Generate the FlashVars string based on whether dataURL has been provided or dataXML.*/ String strFlashVars = ""; Boolean debugModeBool = new Boolean(debugMode); if (strXML.equals("")) { //DataURL Mode strFlashVars = "chartWidth=" + chartWidth + "&chartHeight=" + chartHeight + "&debugMode=" + boolToNum(debugModeBool) + "&dataURL=" + strURL + ""; } else { //DataXML Mode strFlashVars = "chartWidth=" + chartWidth + "&chartHeight=" + chartHeight + "&debugMode=" + boolToNum(debugModeBool) + "&dataXML=" + strXML + ""; } StringBuffer strBuf = new StringBuffer(); // START Code Block for Chart strBuf.append("\t\t<!--START Code Block for Chart-->\n"); strBuf .append("\t\t\t\t<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=8,0,0,0' width='" + chartWidth + "' height='" + chartHeight + "' id='" + chartId + "'>\n"); strBuf.append("\t\t\t\t <param name='allowScriptAccess' value='always' />\n"); strBuf.append("\t\t\t\t <param name='movie' value='" + chartSWF + "'/>\n"); strBuf.append("\t\t\t\t<param name='FlashVars' value=\"" + strFlashVars + "\" />\n"); strBuf.append("\t\t\t\t <param name='quality' value='high' />\n"); strBuf .append("\t\t\t\t<embed src='" + chartSWF + "' FlashVars=\"" + strFlashVars + "\" quality='high' width='" + chartWidth + "' height='" + chartHeight + "' name='" + chartId + "' allowScriptAccess='always' type='application/x-shockwave-flash' pluginspage='http://www.macromedia.com/go/getflashplayer' />\n"); strBuf.append("\t\t</object>\n"); // END Code Block for Chart strBuf.append("\t\t<!--END Code Block for Chart-->\n"); return strBuf.substring(0); } /** * Converts boolean to corresponding integer * @param bool - The boolean that is to be converted to number * @return int - 0 or 1 representing the given boolean value */ public static int boolToNum(Boolean bool) { int num = 0; if (bool.booleanValue()) { num = 1; } return num; } }
FusionCharts中文处理:
jsp方式:
response.setContentType( "text/xml; charset=UTF-8" ); OutputStream outs = response.getOutputStream(); outs.write( new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF} );//UTF-8 BOM outs.write(strXML.getBytes("UTF-8"));//数据 outs.flush(); outs.close(); outs = null; response.flushBuffer(); out.clear(); out = pageContext.pushBody();
java类生成xml数据文件
FileOutputStream out = new FileOutputStream(file,true); out.write(new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF});//UTF-8 BOM out.write(content.getBytes(charset));//数据 out.close();