代码之家  ›  专栏  ›  技术社区  ›  HerrimanCoder

如何使内存中的ASP.NET图表纯粹来自代码隐藏?

  •  0
  • HerrimanCoder  · 技术社区  · 7 年前

    我正在使用 System.Web.UI.DataVisualization.Charting 4.0 在我的ASP.NET应用程序中。在某些环境下,它工作得很好:

    var chart = new Chart();
    
    // Define the chart area
    Grid grid = new Grid();
    ChartArea chartArea = new ChartArea();
    [... setting lots of chartArea properties here...]
    
    ChartArea3DStyle areaStyle = new ChartArea3DStyle(chartArea);
    chart.ChartAreas.Add(chartArea);
    [... more ...]
    
    [... build Series, Legends, etc here ...]
    
    chart.SaveImage("c:\fakepath\myreport.png", ChartImageFormat.Png);
    

    我省略了构建图表的95%代码(非常复杂的逻辑,通过复杂的数据结构进行大量循环),然后将其保存到磁盘上,并从ASPX页面呈现如下:

    <img src="http://fakeserver/fakepath/myreport.png">
    

    在某些客户环境中,这种方法不起作用,因为IIS进程没有写入本地磁盘的权限,并且他们不想打开该磁盘。此外,当多个用户查看(和生成)图表时,它的可伸缩性不是很强,这对于每个用户都是不同的。

    我怎样才能纯粹在内存中生成这个图表?

    我一直在看 ChartHttpHandler 类,但找不到相关的代码示例。理想的情况是,如果我可以按照上面所示的方式构建图表,但是我可以将图表存储在内存(缓存)中,而不是保存到磁盘上。会议?)以这样的方式,我可以指出我的 <img> 在ASPX页中标记为内存中的数据,图像将在该页上呈现。我不想使用声明性ASPX图表构建,因为逻辑太复杂,需要全部用C代码完成。我也不能使用任何将图表图像写入磁盘的方法。

    怎么做到的?

    2 回复  |  直到 7 年前
        1
  •  2
  •   VDWWD    7 年前

    我做了一个完整的例子,包括为其他用户创建图表。但基本上,您将图表图像保存到流而不是文件中。

    using (MemoryStream stream = new MemoryStream())
    {
        chart.SaveImage(stream, ChartImageFormat.Png);
    }
    

    我创建了一个在浏览器中显示图像的通用处理程序。但一旦图像进入流中,您就可以做更多的事情,比如将其放入PDF或电子邮件消息中。

    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Web;
    using System.Web.UI.DataVisualization.Charting;
    using System.Web.UI.WebControls;
    
    namespace YourNameSpace
    {
        public class Handler1 : IHttpHandler
        {    
            public void ProcessRequest(HttpContext context)
            {
                //needed to generate random numbers
                Random rnd = new Random();
    
                //select 12 random numbers between 1 and 50 for column chart
                int[] yRange1 = Enumerable.Range(1, 50).OrderBy(i => rnd.Next()).Take(12).ToArray();
    
                //select 12 random numbers between 1 and 25 for line chart
                int[] yRange2 = Enumerable.Range(0, 25).OrderBy(i => rnd.Next()).Take(12).ToArray();
    
                //select all the month names for the labels
                string[] xLabels = Enumerable.Range(1, 12).Select(i => DateTimeFormatInfo.CurrentInfo.GetMonthName(i)).ToArray();
    
                //create the chart
                Chart chart = new Chart();
    
                //add the bar chart series
                Series series = new Series("ChartBar");
                series.ChartType = SeriesChartType.Column;
                chart.Series.Add(series);
    
                //add the line chart series
                Series series2 = new Series("ChartLine");
                series2.ChartType = SeriesChartType.Line;
                series2.Color = System.Drawing.Color.Purple;
                series2.BorderWidth = 2;
                chart.Series.Add(series2);
    
                //define the chart area
                ChartArea chartArea = new ChartArea();
                Axis yAxis = new Axis(chartArea, AxisName.Y);
                Axis xAxis = new Axis(chartArea, AxisName.X);
    
                //add the data and define color
                chart.Series["ChartBar"].Points.DataBindXY(xLabels, yRange1);
                chart.Series["ChartLine"].Points.DataBindXY(xLabels, yRange2);
                chart.Series["ChartBar"].Color = System.Drawing.Color.Green;
    
                chart.ChartAreas.Add(chartArea);
    
                //set the dimensions of the chart
                chart.Width = new Unit(600, UnitType.Pixel);
                chart.Height = new Unit(400, UnitType.Pixel);
    
                //create an empty byte array
                byte[] bin = new byte[0];
    
                //save the chart to the stream instead of a file
                using (MemoryStream stream = new MemoryStream())
                {
                    chart.SaveImage(stream, ChartImageFormat.Png);
    
                    //write the stream to a byte array
                    bin = stream.ToArray();
                }
    
                //send the result to the browser
                context.Response.ContentType = "image/png";
                context.Response.AddHeader("content-length", bin.Length.ToString());
                context.Response.AddHeader("content-disposition", "attachment; filename=\"chart.png\"");
                context.Response.OutputStream.Write(bin, 0, bin.Length);
            }
    
    
            public bool IsReusable
            {
                get
                {
                    return false;
                }
            }
        }
    }
    

    要在浏览器中显示图像,只需指向 src 对处理程序的帮助。

    <img src="Handler1.ashx" width="600" height="400" />
    
        2
  •  0
  •   Richard Harrison    7 年前

    另一种方法是使用图像标签上的base64编码源代码将图表直接嵌入HTML(通过 src=data:image/base64 );这还有一个优点,即不需要任何进一步的HTTP请求来从Web服务器获取图像。

    例如

    <img src='data:image/png;base64,src='data:image/png;base64,[base64data==]'>
    

    要做到这一点,需要做很多事情。

    用于获取作为操作结果的图像数据的控制器:

       public ActionResult SomeChart(SomeViewModel model, int width, int height)
        {
            model.ChartKey = "SomeChart";
            return Content(getChartImage(() => CreateSomeChart(model, width, height), model, width, height));
        }
    

    这将使用如下方法创建图表:

      private Chart CreateSomeChart(SomeViewModel vm, int width, int height)
      {
          int Id = vm.Id;
          System.Web.UI.DataVisualization.Charting.Chart Chart1 = new System.Web.UI.DataVisualization.Charting.Chart();
          // usual code to draw a chart here...
          return Chart1;
      }
    

    在Razor视图中编写代码以获取图表;在我的视图模型中,我有一个图表列表,这些图表只是要呈现的控制器中的操作结果方法。使用呈现操作将在HTML发送到浏览器并导致图表以base64编码方式内联之前发生。

    它是 public ActionResult SomeChart 应该由html.renderAction方法调用的控制器,对于此示例model.chartList[]=“someChart”

    if (Model.ChartList.Count() > 1)
    {
            xs = 450;
            ys = 300;
    }
    
    foreach(var chart in Model.ChartList)
    { 
        Html.RenderAction(chart, "SomeController", new { model = Model, width = xs, height = ys });
    }
    

    最后一个将它联系在一起的位是getchartimage方法。

      private string getChartImage(Func<Chart> getChart, object routevals, int width, int height, string chartKey="")
        {
            string name = "Chart";
            if (routevals is SomeViewModel)
            {
                var cvm = routevals as SomeViewModel;
                chartKey = cvm.ChartKey;
                name = cvm.ChartKey;
            }
    
            using (var stream = new MemoryStream())
            {
                var Chart1 = getChart();
                Chart1.ImageType = ChartImageType.Png;
                Chart1.Palette = ChartColorPalette.BrightPastel;
                Chart1.SuppressExceptions = true;
    
                // Set chart custom palette
                Chart1.Palette = ChartColorPalette.None;
                Chart1.PaletteCustomColors = Dashboard.Support.ChartPalettes.ColorsBigList;
    
                Chart1.Width = width;
                Chart1.Height = height;
                Chart1.RenderType = RenderType.ImageTag;
    
                Chart1.BackColor = Color.White;
    
                Chart1.BackImageAlignment = ChartImageAlignmentStyle.BottomLeft;
                Chart1.BorderSkin.SkinStyle = BorderSkinStyle.None;
                Chart1.BorderSkin.BorderWidth = 0;
                //Chart1.BackImageTransparentColor
                Chart1.BorderColor = System.Drawing.Color.FromArgb(26, 59, 105);
                Chart1.BorderlineDashStyle = ChartDashStyle.Solid;
                Chart1.BorderWidth = 0;
    
                Chart1.SaveImage(stream, ChartImageFormat.Png);
                string encoded = Convert.ToBase64String(stream.ToArray());
    
                if (Request != null && Request.Browser != null && Request.Browser.Browser == "IE" && Request.Browser.MajorVersion <= 8)
                {
                    /*
                     * IE8 can only handle 32k of data inline so do it via the cached getChart
                     * method - i.e. store the chart in the cache and return a normal image.
                     */
                    if (encoded.Length > 32000)
                    {
                        StringBuilder result = new StringBuilder();
                        if (string.IsNullOrEmpty(chartKey))
                            chartKey = String.Format("{0}{1}", name, Guid.NewGuid());
    
                        System.Web.Helpers.WebCache.Set(chartKey, stream.ToArray());
    
                        result.Append(String.Format("<img width='{0}' height='{1}' src='{3}' alt='' usemap='#ImageMap{2}'>", width, height, name,
                            Url.Action("getChart", new { key = chartKey })));
    
                        return result.ToString() + Chart1.GetHtmlImageMap("ImageMap" + name); ;
                    }
                }
                /*
                 * render using data inline for speed
                 */
                string img = String.Format("<img width='{0}' height='{1}' src='data:image/png;base64,{{0}}' alt='' usemap='#ImageMap{2}'>", Chart1.Width, Chart1.Height, name);
                return String.Format(img, encoded) + Chart1.GetHtmlImageMap("ImageMap" + name);
            }
        }