2012-02-23 33 views
19

這個問題的標題可能會有些誤導,但我不確定最好的標題是什麼(因爲我還不能猜測)。生成HTML畫布圖像數據服務器端?

基本上我正在開發的系統很大程度上依賴於畫布圖。這些圖形是通過JavaScript生成的,並且是使用從API服務器通過ajax提取的數據製作的。

棘手的部分是,我希望能夠通過電子郵件發送這些圖表給這個系統的用戶,而他們根本不必去網頁。因此,雖然我知道可以在瀏覽器中獲得使用JavaScript生成的圖像的Base64值,但如果沒有人運行該JavaScript,那麼該怎麼辦?

我想保留javascript/canvas中生成的圖形,而不是將它們放在通用的服務器端圖形庫(GD,ImageMagick)中。 Canvas圖是動態的,並允許通過javascript進行交互。儘管我不想在電子郵件通知中使用該功能,但我確實希望它們在其他方面相同(至少在外觀上)。

所以問題是,我怎樣才能將這些圖形變成電子郵件?

在這一點上,我唯一的猜測是,我需要從字面上製作一個網站,用於處理「圖形渲染」的AJAX請求,呈現這些圖形,並將結果發送到服務器。然後,我需要一個「服務器」,就在那個網頁上,並且生成圖表。這是唯一的解決方案嗎?

+4

檢出https://github.com/learnboost/node-canvas - 它是一個節點實現的畫布,使用Cairo作爲圖像處理器。 – steveukx 2012-02-23 08:03:06

+0

我不太明白。你是否想要一個與HTML5畫布API兼容的服務器端圖形庫,以便在瀏覽器和服務器上使用相同的代碼? – Thilo 2012-02-23 08:05:04

+0

非常好耶。我對node-canvas和nodejs不熟悉,是解決方案嗎? – GoldenNewby 2012-02-23 08:06:32

回答

5

我使用phantomJs(像node.js但不同)serverside運行完全相同的代碼作爲客戶端,並獲得相同的結果。所有你需要的是一個單獨的exe文件(如webkit獨立web瀏覽器)

以下程序(在Perl中,但應該是可行的,以翻譯成你最喜歡的語言)需要一些數據,插入到網頁(可能是ajax),並將該網頁發送給客戶端,或將其存儲爲臨時文件,然後在同一頁面上啓動PhantomJs。然後請PhantomJs生成一個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; 
} 
+0

我喜歡它,我會給它一個鏡頭 – GoldenNewby 2013-02-10 00:57:26