MattLieber
MattLieber

Reputation: 53

Javascript node js browser refresh upon POST

I am using node canvas in a project; essentially the project is about displaying an image on the browser screen, and being able to control the position of tge image by way of REST API POSTs. This works fine, but upon a POST i want to refresh the browser automatically.

I have looked at:

any help ? Again, the ask is to refresh the browser (or part thereof) upon new coordinates, in the POST method.

Code below.

Cheers, Matt

Node server.js:

//Lets require/import the HTTP module
var http = require('http');
var express = require('express');
var app = express();
var bodyParser = require('body-parser')
var fs = require('fs')
var path = require('path')

var draw = require('./draw_badge');

var robot1;
var robot1_xcoord = 30;    
var robot1_ycoord = 100;    

var robot2;
var robot2_xcoord = 50;
var robot2_ycoord = 30;    

/** bodyParser.urlencoded(options)
 * Parses the text as URL encoded data (which is how browsers tend to send form data from regular forms set to POST)
 * and exposes the resulting object (containing the keys and values) on req.body
 */
app.use(bodyParser.urlencoded({
    extended: true
}));
/**bodyParser.json(options)
 * Parses the text as JSON and exposes the resulting object on req.body.
 */
app.use(bodyParser.json());

/** -------- Start -----
 *
 */



  {
    app.get('/', function (req, res) {

   console.log("Xcoord: " + robot1_xcoord);
  res.setHeader('Content-Type', 'image/png');
  // redraw everything
  draw(robot1_xcoord,robot1_ycoord, robot2_xcoord,robot2_ycoord).pngStream().pipe(res);

});


 // Getting a POST
app.post('/', function (req, res) {
  console.log(req.body.id);
if (req.body.id=="1")
{
  console.log("robot1 change");
  robot1_xcoord = req.body.xcoordinate;
  robot1_ycoord = req.body.ycoordinate;
}
else
 if (req.body.id=="2")
{
  console.log("robot2 change");

  robot2_xcoord = req.body.xcoordinate;
  robot2_ycoord = req.body.ycoordinate;
}
 // draw(robot1_xcoord,robot1_ycoord, robot2_xcoord,robot2_ycoord).pngStream().pipe(res);
//res.redirect('localhost:5000');
res.send('Got a POST request' );

// try
//res.redirect(req.get('referer'));
/*
 return http.get({
        host: 'localhost',
        path: '/'
    }, function(response) {
        // Continuously update stream with data
        var body = '';
        response.on('data', function(d) {
            body += d;
        });
        response.on('end', function() {

            // Data reception is done, do whatever with it!
            var parsed = JSON.parse(body);

        });
    });
*/

});
 // Main app - Listen
app.listen(process.env.PORT || 5000, function () {
  console.log('Example app listening !');
});

and

draw_badge.js:

var Canvas = require('canvas')
var fs = require('fs')


function draw_badge(x,y) {


   var x, y, i

  ctx.clearRect(0, 0, 120, 120)

  ctx.save()

  ctx.translate(160, 160) 
  ctx.beginPath()
  ctx.lineWidth = 14
  ctx.strokeStyle = '#325FA2'
  ctx.fillStyle = '#eeeeee'

  ctx.arc(x, y, 42, 0, Math.PI * 2, true)
  ctx.stroke()
  ctx.fill()


 return canvas;
}


function draw_robot(x,y) {

var Image = Canvas.Image
var canvas = new Canvas(600, 600)
var ctx = canvas.getContext('2d')


var img = new Image()
img.src = canvas.toBuffer()
ctx.drawImage(img, 0, 0, 50, 50)
ctx.drawImage(img, 50, 0, 50, 50)
ctx.drawImage(img, 100, 0, 50, 50)

img.src = fs.readFileSync('./kuka.png')
ctx.drawImage(img, 100, 0, img.width , img.height )

//img = new Image()
img.src = fs.readFileSync('./robot.jpeg')
ctx.drawImage(img, x, y, img.width / 2, img.height / 2)
// new


canvas.createPNGStream().pipe(fs.createWriteStream('./image-robot.png'))
return canvas

}

function draw(x1,y1,x2,y2)
{
  Image = Canvas.Image,
      canvas = new Canvas(600, 600),
      ctx = canvas.getContext('2d');
  canvas = draw_robot(x1,y1);
  canvas = draw_badge(x2,y2);
  return canvas;
}

module.exports = draw;

Upvotes: 0

Views: 1618

Answers (1)

anx
anx

Reputation: 302

They way you are trying cannot work

  • The image that has been delivered to the browser cannot be refreshed dynamically, simply because it is an image. Once the server has delivered and the client loaded their work is done.
  • Attempting to write to the request (which may be one of hundreds) will of course result in a "write after end", because the end of the request was when the image first loaded in your browser.
  • the express res.redirect function cannot be called post facto (after the request) either, also it would immediately redirect, which you are not looking for.

Simple solution: Refresh via HTTP-Header (correctly)

app.get('/', function (req, res) {
   console.log("Xcoord: " + robot1_xcoord);
   res.setHeader('Content-Type', 'image/png');
   // refresh every second
   res.setHeader('Refresh','1');
   // redraw everything
   draw(robot1_xcoord,robot1_ycoord,robot2_xcoord,robot2_ycoord).pngStream().pipe(res);

});

Real solution: streaming image

You could supply an actual image-stream. The idea being that your request to the picture would never be closed, and when you alter the picture via your REST-API, the next picture of the stream would be delivered. In theory, your browser would display the last complete frame it got, thus kind of "update" your image in the browser window. This would be the real solution here, but might be expensive in terms of time wasted on the implementation. This would take some re-arrangements in your code.

caveat: firefox only, chrome support has been dropped as I just learned :/

server.js

//Lets require/import the HTTP module
var http = require('http');
var express = require('express');
var app = express();
var bodyParser = require('body-parser')
var fs = require('fs')
var path = require('path')

var draw = require('./draw_badge');

var robot1;
var robot1_xcoord = 30;
var robot1_ycoord = 100;

var robot2;
var robot2_xcoord = 50;
var robot2_ycoord = 30;

// An array to hold a list of active clients
var clients = [];

// draw an initial version of your buffer
var imageData = draw(robot1_xcoord, robot1_ycoord, robot2_xcoord, robot2_ycoord).toBuffer(undefined, 3, canvas.PNG_FILTER_NONE);
// get the size in bytes as well, we'll need it
var length = imageData.byteLength;

/** bodyParser.urlencoded(options)
 * Parses the text as URL encoded data (which is how browsers tend to send form data from regular forms set to POST)
 * and exposes the resulting object (containing the keys and values) on req.body
 */
app.use(bodyParser.urlencoded({
  extended: true
}));
/**bodyParser.json(options)
 * Parses the text as JSON and exposes the resulting object on req.body.
 */
app.use(bodyParser.json());

/** -------- Start -----
 *
 */

app.get('/', function(req, res) {
  // prepare header so that the browser will wait for arbitray updates
  res.writeHead(200, {
    'Content-Type': 'multipart/x-mixed-replace; boundary=--NEW_IMAGE_HERE',
    'Cache-Control': 'no-cache',
    'Connection': 'close',
    'Pragma': 'no-cache'
  });
  var on_update = function(imageData, length) {
      try {
        console.log("Updating client.. bytes:", length)
        res.write("--NEW_IMAGE_HERE\r\n");
        res.write("Content-Type: image/png\r\n");
        res.write("Content-Length: " + length + "\r\n\r\n");
        res.write(imageData);
      } catch (e) { // in case of an error remove from the clients array
        console.log("Error: ", e);
        clients.splice(clients.indexOf(on_update), 1);
      }
    }
    // remove on disconnect
  res.on('close', function() {
    console.log("Disconnected");
    clients.splice(clients.indexOf(on_update), 1);
  });
  // send the client our last cached version of the image
  on_update(imageData, length);
  // add our update function to the array of clients
  clients.push(on_update);
});

// Getting a POST
app.post('/', function(req, res) {
  console.log(req.body.id);
  if (req.body.id == "1") {
    console.log("robot1 change");
    robot1_xcoord = req.body.xcoordinate;
    robot1_ycoord = req.body.ycoordinate;
  } else
  if (req.body.id == "2") {
    console.log("robot2 change");
    robot2_xcoord = req.body.xcoordinate;
    robot2_ycoord = req.body.ycoordinate;
  }
  res.send('Got a POST request');
  // redraw everything into the buffer
  imageData = draw(robot1_xcoord, robot1_ycoord, robot2_xcoord, robot2_ycoord).toBuffer(undefined, 3, canvas.PNG_FILTER_NONE);
  length = imageData.byteLength;
  // notify active clients
  for (on_update of clients) {
    on_update(imageData, length);
  }
});
// Main app - Listen
app.listen(process.env.PORT || 5000, function() {
  console.log('Example app listening !');
});

Upvotes: 1

Related Questions