生成服务器端的HTML Canvas图像数据?

28
这个问题的标题可能会有点误导性,但我不确定最好的标题是什么(因为我还不知道解决方案)。
基本上,我正在开发的系统严重依赖于canvas图。 这些图表是通过javascript生成的,并使用通过ajax从API服务器提取的数据制作而成。
棘手的部分是,我希望能够向该系统的用户发送这些图形的电子邮件,而无需他们实际上去网页。 因此,虽然我知道可以在浏览器中获取用javascript生成的图像的Base64值,但如果没有人运行该javascript怎么办?
我想保持javascript / canvas生成的图表,而不是在常见的服务器端图形库(如GD,ImageMagick)中制作它们。 Canvas图表是动态的,并允许通过javascript进行交互。 虽然我不希望在电子邮件通知中拥有那种功能,但除此之外,我希望它们在外观上都是相同的。
那么问题是,我如何将这些图形放入电子邮件中?
目前,我唯一的猜测是,我需要真正制作一个网站,该网站请求“要呈现的图表”,呈现这些图表,并将结果发送到服务器。 然后我需要一个“服务器”,只需坐在那里并输出图表即可。 这是唯一的解决方案吗?

4
请查看https://github.com/learnboost/node-canvas——这是一个使用Cairo作为图像处理器的基于Node实现的画布。 - steveukx
我不太明白。您是否需要一种服务器端图形库,与HTML5画布兼容,并且可以在浏览器和服务器上使用相同的代码? - Thilo
差不多是的。我对node-canvas和nodejs不熟悉,这是解决方案吗? - GoldenNewby
2
我一直在使用node-canvas(以及其上的fabric.js)在服务器上从canvas生成图像,效果非常好。 - kangax
1个回答

9

我使用PhantomJs(类似于node.js但不同)服务器端运行与客户端完全相同的代码,并获得相同的结果。你只需要一个单独的exe文件(就像一个独立的webkit浏览器)

下面的程序(用Perl编写,但应该可以翻译成您喜欢的语言)获取一些数据,将其插入到web页面中(可能是ajax请求),然后将该网页发送给客户端,或将其存储为临时文件,并在同一页上启动PhantomJs。然后请PhantomJs生成一个jpg,这个jpg随后被选中(并在这种情况下发送给客户端)。

#!/usr/bin/perl

use strict;
use File::Temp;
$|=1;
#this script returns a graph, either as html +js web page to render client side,
#or renders the same page server side, and returns the jpg image.

#files needed:
#.\phantom_srv_client.pl  #this script
#.\phantomjs.exe          #the webkit runtime stand alone file, from http://phantomjs.org/
#.\Scripts\excanvas.min.js #canvas simulator for IE8-
#.\Scripts\jquery.min.js   #jQuery as we know it
#.\Scripts\jquery.jqplot.min.js #graph library on top of jQuery from http://www.jqplot.com/ (Flot or any can be used)


#do we want client side rendering (html + js), or server side rendering (jpg)
#jpg seems to render nicer than png on some pages?
use CGI;
my $show_as_jpg = CGI::param("jpg");

#path to javascript libraries (jQuery etc). 
#Must be absolute file location for server rendering, relative for web
use FindBin;
my $script_path = $show_as_jpg 
    ? $FindBin::Bin."/Scripts" 
    : './Scripts';


#data to send to graph (two sets)
my $data = [[2,5,4], [6,4,5]];

#use json to get this as a javascript text
my $json_data;
eval {require JSON; $json_data=JSON::to_json($data)};
#in case JSON is not installed, get the json/javascript data manually (just for demo)
$json_data ||= "[[2,5,4], [6,4,9]]"; #here 9 at the end to see a difference

#The following is the web page that renders the graph, client or server side 
#(links to scripts must be abolute to work serverside, as temp-files may go anywhere, $script_path keeps track of that)
#$json_data is the Perl data structure converted to JSON (aka javascript, but not)
my $graph_html =qq|
<!DOCTYPE html>
<html>
<head>
    <!--[if lt IE 9]><script language="javascript" type="text/javascript" src="$script_path/excanvas.min.js"></script><![endif]-->
    <script class="include" type="text/javascript" src="$script_path/jquery.min.js"></script>
    <script class="include" type="text/javascript" src="$script_path/jquery.jqplot.min.js"></script>

    <script class="code" type="text/javascript" language="javascript">
        jQuery(document).ready(function(){
            /*data from perl (\$json_data) inserted here */
            var data = $json_data;
            jQuery.jqplot("chart1", data );
        });
    </script>
    </head>
<body>
    <div id="chart1" style="width:600px; height:400px;"></div>
    <a href='?jpg=1'>View as jpg</a>
</body>
</html>
|;


#this is the javascript that tells PhantomJs what to do (ie open a doc and render it to bitmap)
my $phantom_doc_js =qq|
    var system = require('system');
    //read from commandline which files to open, and write to
    var open_doc = system.args[1];
    var return_doc = system.args[2];
    var page = require('webpage').create();
    page.open(open_doc, function () {
        page.render(return_doc);
        phantom.exit();
    });
|;

#see if we shall render this page serverside
if ($show_as_jpg) {
    #get temporary filenames with related file handlers
    #where to put phantomjs script (generic so could be a static file)
    my ($phantom_doc_filehandler, $phantom_doc_filename) = File::Temp::tempfile(  SUFFIX => '.js', TMPDIR => 1);
    #where to put web page with data to render and ref to javascripts etc
    my ($phantom_graph_filehandler, $phantom_graph_filename) = File::Temp::tempfile(SUFFIX => '.html', TMPDIR => 1);
    #also get a filename with no handler, so phantomjs can return the jpg file. Extention must be .jpg!
    my (undef, $image_filename) = File::Temp::tempfile( SUFFIX => '.jpg',TMPDIR => 1, OPEN => 0);

    #store file content and close files
    print $phantom_doc_filehandler $phantom_doc_js; close $phantom_doc_filehandler;
    print $phantom_graph_filehandler $graph_html;   close $phantom_graph_filehandler;

    #now call PhantomJs with filenames to read from and write to.
    #Next version should support piping, which would simplify a lot

    #use absolute path to phantomjs.exe in case web-server does not use current path
    system($FindBin::Bin.'\\phantomjs', $phantom_doc_filename, $phantom_graph_filename, $image_filename) == 0 
        or die "system failed: $?";

    #read the entire image file
    my $img = slurp_file($image_filename);
    print "Content-Type: image/jpeg\nPragma: no-cache\n\n".$img;

    #The temp files are no more needed
    unlink $phantom_doc_filename, $phantom_graph_filename, $image_filename;

} else { # just render client side
    print "Content-Type: text/html\nPragma: no-cache\n\n".$graph_html;
}

#slurp is not always std perl   
sub slurp_file{
  my $filename = shift;
  my $string;
  local $/ = undef;
  open FILE, $filename or die "Couldn't open file: $!";
  binmode FILE;
  $string = <FILE>;
  close FILE;
  return $string;
}

我喜欢它,我会试一试。 - GoldenNewby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接