【问题标题】:generate server-side google charts as images生成服务器端谷歌图表作为图像
【发布时间】:2014-07-24 08:19:09
【问题描述】:

我刚刚开始使用(新的)Google Charts api,它非常酷。我正在使用此 api 成功创建图表。

但是,我遇到了吞吐量挑战。在我的应用程序中,我根据从 NOAA 获取的实时数据生成了三个图表。获取数据、将其处理成图表形式然后在客户端绘制图表所花费的时间是一种令人无法忍受的缓慢用户体验。

所以我的想法是在(托管)服务器上定期(每 15-30 分钟)生成图表,然后只向访问者提供最近的图像。

我查看了 phantomjs(按照 this post 中的建议),但它看起来像是使用了一个 .exe 文件,我无法将它上传到我的共享主机。

还有 this thread 用于专有解决方案(Highcharts),但我想先探索开源替代方案,然后再走 Highcharts 路径。

其他解决方案侧重于允许用户将呈现的图表另存为图像,但我的目标是永远不要在浏览器中呈现图表或让任何服务器加载,除了在页面请求时包含图像。

我还没有看到任何处理动态生成的图表的东西,这些图表“自动”转换为在页面呈现时“自动”提供的图像。

总之,这是我试图拼凑的三个部分:

1) 从第三方(在这种情况下为 NOAA)提取数据并将数据呈现为 Google 图表(完成,这里没有问题) 2)自动将每个渲染图表转换为图像,服务器端并创建图像url 3)在渲染前将图表的图片URL(会经常刷新)粘贴到网页的html中(通过php)

附:可以为每个图表图像设置一个静态 url...我没有创建图像存档...

有什么建议吗?我错过了什么吗?

【问题讨论】:

  • 我会使用缓存。每次页面加载时,检查是否有缓存或者缓存的版本是否太旧;如果是这样,生成一个新的。这是共享的唯一选项。但是您是否对实际如何将此图表转换为图像更感兴趣?如果是这样,图表是什么格式? SVG?
  • 我没有使用缓存,所以请原谅我的幼稚问题。你在考虑服务器端吗?所以第一个请求页面的用户仍然会受到性能影响,但后续用户(理论上)不会?至于您评论的第二部分,Google Chart api 本身会创建一个 SVG 图表。我希望呈现给用户的页面包含与用户页面请求异步创建的图像。
  • 可视化 API 完全是客户端的,因此您必须在浏览器中渲染图表,然后将图像数据发送到服务器进行保存。但是,大多数图表的渲染速度都很快,因此我会尝试将您的数据查询缓存到 NOAA,并将数据发送到客户端以呈现图表,然后再研究静态图像解决方案。
  • @asgallant - 感谢您的建议!您的评论促使我调查每个过程的相对经过时间。事实上,从 NOAA 下载和按摩数据大约需要 3100 毫秒,而实际下载谷歌图表 js 和渲染图表大约需要 400 毫秒!所以我将把注意力转向数据采集与转换!如果您将您的评论作为答案写出来,我会接受!
  • 更新:我已经实施了 asgallant 的建议,并将页面加载时间从 6 秒以上缩短到 3 秒左右。如果我可以消除图表的实时渲染,下一个 50% 的改进(将页面加载时间减少到 1.5 秒)将会到来......

标签: javascript php image charts google-visualization


【解决方案1】:

使用Watir 加载网页并保存图像。 Watir 是一个基于 Ruby 的测试平台,旨在测试网页。我会做以下事情。

  1. 创建一个呈现图表的网页。这不会是为了 公开使用,仅用于生成图表图像。
  2. 为每个图表添加调用 getImageURI() 的 Javascript 以获取 PNG 数据。
  3. 使用 Ajax 在服务器上调用 PHP 脚本。传入PNG数据 并将其保存到文件中。
  4. 在 Watir 中编写一个脚本,该脚本只需打开浏览器并加载您的 网页。
  5. 根据需要安排 Watir 脚本的运行频率。

Watir 脚本可以在任何本地计算机(PC 或 Mac)甚至服务器上运行。

这是通过 Ajax 将 PNG 数据发送到 PHP 脚本的 Javascript。只是sn-p获取图像数据并发送出去,并不是一个完整的解决方案。

// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.PieChart(chartDiv);
// Wait for the chart to finish drawing before calling the getImageURI() method.
google.visualization.events.addListener(chart, 'ready', function () {
    pieDone=1;
    piePNG=chart.getImageURI();
    saveChartPNGs(piePNG); 
});
chart.draw(data, options);

function saveChartPNGs(png)
{
  var jsonSavePNG = $.ajax({
    type: "POST",
    url: pngSaveUrl,
    dataType:"json",
    data: 'png=' + png
  }).done();
}

此 PHP 代码处理 Ajax 调用并保存文件。

if (isset($_REQUEST['png']))
{
    // Remove header string
    $data=str_replace('data:image/png;base64,','',$_REQUEST['png']);
    // Restore data to original format; convert space to '+'
    $data=str_replace(' ','+',$data);
    // Save PNG file
    $outFile=DOC_ROOT.'/atest.png';
    // Decode base 64 before saving
    $fres=file_put_contents($outFile, base64_decode($data));
}

在您的本地计算机上,Watir 脚本(实际上是用 Ruby 编写的)很简单。

# Load watir
require 'watir-webdriver'
require "watir-webdriver/extensions/alerts"

# Launch the browser; common choices: chrome, firefox, ie.
# Headless browsers are available.
browser = Watir::Browser.new :chrome # chrome browser window

# Load the web page
browser.goto 'http://domain.net/page/'

为了完全自动化 Watir 端,我会编写图表呈现网页,以便在完成保存文件后加载新页面。您可以让 Watir 在浏览器中检查该页面,然后退出,直到它再次执行。

如果您可以安排在您的服务器上安装 Ruby、Watir 和浏览器,那么您可以使用 cron 作业来自动化整个场景。

【讨论】:

  • 你说 watir 可以在服务器上运行,但这似乎做了一些假设,例如它有一个可以运行 Chrome 或其他网络浏览器的 GUI?对于许多无头服务器来说,这不是真的。
  • @Michael,我不知道什么是无头服务器,但是 watir 可以启动无头浏览器,这意味着没有窗口显示。因此不需要在服务器上安装 GUI(如 X Windows)。
【解决方案2】:

我刚刚发布了一个相关的开源项目Google Charts Node,它可以在 Puppeteer(一种无头 Chromium 浏览器)中呈现图表图像。

它可以用作NPM library 或Web 服务。下面是一个使用 NPM 库的例子:

const GoogleChartsNode = require('google-charts-node');

function drawChart() {
  // Set up your chart here, just like in the browser
  // ...

  const chart = new google.visualization.BarChart(container);
  chart.draw(data, options);
}

// Render the chart to image
const image = await GoogleChartsNode.render(drawChart);

您的问题已被标记为 PHP,因此我会注意到它也可作为可用于任何语言的托管 Web 服务。你可以阅读更多here

例如:

// Generate the image
$url = 'https://quickchart.io/google-charts/render';
$data = array(
  'width' => 600,
  'height' => 300,
  'packages' => array('gannt'),
  'code' => 'function drawChart() {...}'
);

$postdata = json_encode($data);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
$result = curl_exec($ch);
curl_close($ch);

// Write it to file
$fp = fopen('/tmp/image.png','x');
fwrite($fp, $raw);
fclose($fp);

现在您可以将此 image 缓冲区保存为文件或将其作为 HTTP 响应返回等。

主要注意事项是:

  1. 并非所有图表都支持 getImageURI,因此库会在发生这种情况时让 puppeteer 截取屏幕截图。
  2. 很慢!但是,如果您必须使用 Google 图表作为要求,那么您真的没有其他选择。如果您能够切换图表库,我建议您通过 QuickChart 使用 Chart.js 之类的开放格式。

您可以在Github project 上查看生成服务器端 Google 图表的完整源代码。

【讨论】:

    猜你喜欢
    • 2017-04-17
    • 2012-02-23
    • 2011-08-09
    • 1970-01-01
    • 2015-10-07
    • 2013-07-30
    • 2015-07-31
    • 1970-01-01
    • 2011-06-19
    相关资源
    最近更新 更多