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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9409106/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 06:34:49  来源:igfitidea点击:

Generating HTML Canvas image data server-side?

javascripthtmlcanvas

提问by GoldenNewby

The title of this question may be slightly misleading, but I'm not sure what the best title would be (since I can't guess at a solution yet).

这个问题的标题可能有点误导,但我不确定最好的标题是什么(因为我还无法猜测解决方案)。

Basically the system I am developing relies heavily on canvas graphs. These graphs are generated through javascript, and are made using data pulled via ajax from an API server.

基本上,我正在开发的系统在很大程度上依赖于画布图。这些图形是通过 javascript 生成的,并使用通过 ajax 从 API 服务器中提取的数据制作。

The tricky part is, I'd like to be able to email these graphs to users of this system, without them actually having to go to the web page at all. So while I'm aware that it is possible to get the Base64 value of an image generated with javascript in a browser, what about if no one is there to run that javascript?

棘手的部分是,我希望能够将这些图表通过电子邮件发送给该系统的用户,而他们实际上根本不必访问网页。因此,虽然我知道可以在浏览器中获取使用 javascript 生成的图像的 Base64 值,但如果没有人运行该 javascript 呢?

I'd like to keep the graphs generated in javascript/canvas, rather than making them in a common server-side graphics library (GD, ImageMagick). The Canvas graphs are dynamic, and allow for interaction via javascript. Though I don't want that functionality in the email notification, I do want them to be identical otherwise (at least in appearance).

我想保留在 javascript/canvas 中生成的图形,而不是将它们制作在通用的服务器端图形库(GD、ImageMagick)中。Canvas 图形是动态的,并允许通过 javascript 进行交互。虽然我不希望在电子邮件通知中使用该功能,但我确实希望它们在其他方面是相同的(至少在外观上)。

So the question is, how can I get these graphs into an email?

所以问题是,我怎样才能将这些图表放入电子邮件中?

At this point, my only guess is that I'd need to literally make a website that does AJAX requests for "graphs to render", renders these graphs, and sends the results to the server. Then I'd need a "server" that just sits there on that web page and churns out graphs. Is that the only solution here?

在这一点上,我唯一的猜测是我需要从字面上创建一个网站,该网站对“要呈现的图形”执行 AJAX 请求,呈现这些图形,并将结果发送到服务器。然后我需要一个“服务器”,它就在那个网页上并生成图表。这是这里唯一的解决方案吗?

采纳答案by FtLie

I used phantomJs (like node.js but different) serverside to run exactly the same code as client side, and get the same result. all you need is one single exe-file (like a webkit stand alone web brower)

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

The following program (in Perl, but should be feasible to translate to you favourite language) takes some data, inserts into a web-page (could be ajax'ed) and either sends that web page to the client, or stores it as a temporary file, and starts PhantomJs on the same page. Then ask PhantomJs to generate a jpg, that is then picked up (and in this case sendt to the client).

以下程序(在 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;
}