Generating HTML Canvas image data server-side?

后端 未结 1 500
挽巷
挽巷 2021-02-01 18:24

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

相关标签:
1条回答
  • 2021-02-01 19:05

    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)

    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).

    #!/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 讨论(0)
提交回复
热议问题