Reputation: 53
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:
npm packages (browser-refresh, etc) ; but they require to put some code in the client page, but i dont have any html client page.
calling res.redirect ; this doesnt seem to do anything.
calling my draw() method in the POST method: i m getting an error about 'write after end' .
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
Reputation: 302
They way you are trying cannot work
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