Javascript node js browser refresh upon POST - node.js

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;

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 !');
});

Related

Forcing Client to Stop AJAX from Node.js Server

I looked at several SO posts trying to find a way to make a Node.js server tell a client to stop uploading after a certain file size has been reached. The most promising of these is ed-ta's technique at Avoiding further processing on busyboy file upload size limit.
Per ed-ta, my Node.js server seemed to be doing what it should. The server sent the 455 status code as soon as the size limit was reached and stopped accepting any more data. Unfortunately, my client kept processing the file until it was completely done anyway. This is a less than ideal experience when the user tries to upload extremely large files since the client doesn't alert the user that the threshold is reached until the AJAX request is completely done.
How do I get the client to see the 455 status code in a timely manner?
I tried checking for the 455 status inside xhr.onreadystatechange, but I can't seem to find that information from inside onreadystatehange even if the server has already sent the 455 in the response 1. Also, the onreadystatechange event doesn't seem to trigger until after the entire file has been processed by the client anyway.
I have tried to simplify the problem by getting rid of the irrelevant details and my current demo code follows:
Server.js
// This code is based on
// https://stackoverflow.com/questions/23691194/node-express-file-upload
//
// [1] - https://stackoverflow.com/questions/39681966/
// avoiding-further-processing-on-busyboy-file-upload-size-limit
//
// [2] - https://stackoverflow.com/questions/18310394/
// no-access-control-allow-origin-node-apache-port-issue
//
// [3] - https://stackoverflow.com/questions/39681966/
// avoiding-further-processing-on-busyboy-file-upload-size-limit
//
// [4] - https://stackoverflow.com/questions/44736327/
// node-js-cors-issue-response-to-preflight-
// request-doesnt-pass-access-control-c
var express = require('express');
var busboy = require('connect-busboy');
var fs = require('fs-extra');
var cors = require('cors'); // [4]
const app = express();
// See [2][4]
app.use(
function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "null");
res.setHeader("Access-Control-Allow-Methods", "GET, PUT, POST")
next();
}
);
app.options('*', cors());
app.use(
busboy({ limits: { files: 1, fileSize: 500000000 } }) // [1]
);
app.post('/uploadEndpoint', function (req, res, next) {
var fStream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldName, file, filename) {
console.log("Uploading: " + filename);
var destPath = './' + filename;
fStream = fs.createWriteStream(destPath);
file.pipe(fStream);
// ed-ta [3]
// Despite being asynchronous limit_reach
// will be seen here as true if it hits max size
// as set in file.on.limit because once it hits
// max size the stream will stop and on.finish
// will be triggered.
var limit_reach = false;
req.busboy.on('finish', function() {
if(!limit_reach){
res.send(filename + " uploaded");
}
});
file.on('limit', function() {
fs.unlink(destPath, function(){
limit_reach = true;
res.status(455).send("File too big.");
console.log('Telling client to stop...');
});
});
});
})
app.listen(8000);
test.html
<!DOCTYPE html>
<!--
This code is based off timdream and Basar at
https://stackoverflow.com/questions/6211145/
upload-file-with-ajax-xmlhttprequest
[1] - https://stackoverflow.com/questions/49692745/
express-using-multer-error-multipart-boundary-not-found-request-sent-by-pos
-->
<html>
<head>
<meta charset="utf-8" />
<script>
function uploadFile() {
var progress = document.getElementById("output");
progress.innerText = "Starting";
var fileCtl = document.getElementById("theFile");
var file = fileCtl.files[0];
var xhr = new XMLHttpRequest();
// timdream
var formData = new FormData();
formData.append('theFile', file);
xhr.upload.onprogress = (progressEvent) => {
var percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
progress.innerText =
"Percent Uploaded: " + percentCompleted + "%";
};
xhr.onreadystatechange = function(e) {
if (this.status === 455) {
alert('Sorry, file was too big.');
}
};
xhr.open('post', 'http://localhost:8000/uploadEndpoint', true);
// Apparently this line causes issues Multipart Boundary not
// found error [1]
// xhr.setRequestHeader("Content-Type","multipart/form-data");
// timdream
xhr.send(formData);
}
</script>
</head>
<body>
<input type="file" id="theFile" name="theName" /><br />
<div id="output">Upload Progress</div>
<input type="button" id="theButton"
onclick="uploadFile();" value="Send" />
</body>
</html>
1 - I could setup another endpoint on the Node.js server and use AJAX to poll that endpoint for the current status inside the client's onprogress but that seems like a kludgy solution that would waste bandwidth.
To get around the problem described above, I wound up using a separate WebSocket channel to send a message from the server back down to the client to tell the said client to stop the upload. I then called abort on the client's XMLHttpRequest object per the Mozilla docs.
Final sample code looks like this:
Server.js
// This code is based on
// https://stackoverflow.com/questions/23691194/node-express-file-upload
//
// [1] - https://stackoverflow.com/questions/39681966/
// avoiding-further-processing-on-busyboy-file-upload-size-limit
//
// [2] - https://stackoverflow.com/questions/18310394/
// no-access-control-allow-origin-node-apache-port-issue
//
// [3] - https://stackoverflow.com/questions/39681966/
// avoiding-further-processing-on-busyboy-file-upload-size-limit
//
// [4] - https://stackoverflow.com/questions/44736327/
// node-js-cors-issue-response-to-preflight-
// request-doesnt-pass-access-control-c
var express = require('express');
var busboy = require('connect-busboy');
var fs = require('fs-extra');
var cors = require('cors'); // [4]
var ws = require('ws');
var WebSocketServer = ws.WebSocketServer;
var g_ws;
// BEGIN FROM: https://www.npmjs.com/package/ws
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function connection(ws) {
g_ws = ws;
});
// END FROM: https://www.npmjs.com/package/ws
const app = express();
// See [2][4]
app.use(
function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "null");
res.setHeader("Access-Control-Allow-Methods", "GET, PUT, POST")
next();
}
);
app.options('*', cors());
app.use(
busboy({ limits: { files: 1, fileSize: 300000000 } }) // [1]
);
app.post('/uploadEndpoint', function (req, res, next) {
var fStream;
req.pipe(req.busboy);
req.busboy.on('file', function (fieldName, file, fileNameObject) {
var filename = fileNameObject.filename;
console.log("Uploading: " + filename);
var destPath = './' + filename;
fStream = fs.createWriteStream(destPath);
file.pipe(fStream);
// ed-ta [3]
// Despite being asynchronous limit_reach
// will be seen here as true if it hits max size
// as set in file.on.limit because once it hits
// max size the stream will stop and on.finish
// will be triggered.
var limit_reach = false;
req.busboy.on('finish', function() {
var message;
if(!limit_reach){
message = 'success';
res.send(filename + " uploaded");
} else {
message = 'TooBig';
}
g_ws.send(message);
});
file.on('limit', function() {
fs.unlink(destPath, function(){
limit_reach = true;
res.status(455).send("File too big.");
console.log('Telling client to stop...');
// https://www.npmjs.com/package/ws
g_ws.send("TooBig");
});
});
});
})
app.listen(8000);
test.html
<!DOCTYPE html>
<!--
This code is based off timdream and Basar at
https://stackoverflow.com/questions/6211145/
upload-file-with-ajax-xmlhttprequest
[1] - https://stackoverflow.com/questions/49692745/
express-using-multer-error-multipart-boundary-not-found-request-sent-by-pos
-->
<html>
<head>
<meta charset="utf-8" />
<script>
function uploadFile() {
var progress = document.getElementById("output");
progress.innerText = "Starting";
var fileCtl = document.getElementById("theFile");
var file = fileCtl.files[0];
var xhr = new XMLHttpRequest();
// https://developer.mozilla.org/en-US/docs/Web/API/WebSocket
const socket = new WebSocket('ws://localhost:8080');
socket.addEventListener('message', function (event) {
if (event.data === 'TooBig') {
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort
xhr.abort();
alert('Server says file was too big.');
} else if (event.data === 'success') {
alert('File uploaded sucessfully.');
} else {
alert('Unknown server error');
}
socket.close();
});
// timdream
var formData = new FormData();
formData.append('theFile', file);
xhr.upload.onprogress = (progressEvent) => {
var percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
progress.innerText =
"Percent Uploaded: " + percentCompleted + "%";
};
xhr.onreadystatechange = function(e) {
if (this.status === 455) {
alert('Sorry, file was too big.');
}
};
xhr.open('post', 'http://localhost:8000/uploadEndpoint', true);
// Apparently this line causes issues Multipart Boundary not
// found error [1]
// xhr.setRequestHeader("Content-Type","multipart/form-data");
// timdream
xhr.send(formData);
}
</script>
</head>
<body>
<input type="file" id="theFile" name="theName" /><br />
<div id="output">Upload Progress</div>
<input type="button" id="theButton"
onclick="uploadFile();" value="Send" />
</body>
</html>
This is not the exact code I used for my solution but this simplified working demo illustrates the concept.
BTW: Filesize limit was lowered to 300000000 on the server to make testing easier but that doesn't matter.

Getting 413 Error when calling Express Api from Swift

When I try to access my Express endpoint that I made in node from my iOS app, I get a 413 error, saying that the payload is too large. Here is my Node code.
router.post('/images/tags/nutritionalInformation/image/base64encoded', function (req, res) {
var base64Stream = req.body.imageString;
var imgDecodedBuffer = decodeBase64Image(base64Stream);
// write to image file
var prefix = guid().toString() + ".jpg";
var filePath = './uploads/' + prefix;
return s3fsImpl.writeFile(prefix, imgDecodedBuffer.data).then(function () {
var s3BaseUrl = "https://s3.amazonaws.com/app/" + prefix;
console.log(s3BaseUrl);
app.models.predict('KEY', s3BaseUrl).then(
function (response) {
var responseJson = JSON.stringify(response.data.outputs[0].data.concepts);
var data = collectTags(responseJson);
data.then(function (value) {
res.json(value);
});
},
function (err) {
console.log(err);
});
});
})
I tested this out in postman and when I attach the base64 encoded string in the body, it works fine! However, when I try to send a POST call from Swift using the following Code :
func getTags() {
let image : UIImage = UIImage(named: "burger.jpg")!
let imageData = UIImageJPEGRepresentation(image, 1.0)
// base64 encode imagedata
let prefix = "data:image/jpeg;base64,"
let strBase64:String = prefix + (imageData?.base64EncodedString(options: .lineLength64Characters))!
// Call tagging api https://foodenodeapp.herokuapp.com/api/images/tags/nutritionalInformation/image/base64encoded
var request = URLRequest(url: URL(string: "https://foodenodeapp.herokuapp.com/api/images/tags/nutritionalInformation/image/base64encoded")!)
request.httpMethod = "POST"
let poststring = "imageString=" + strBase64
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(error)")
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
print(data)
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(responseString)")
}
task.resume()
}
However, when I print out the responseString in the Swift Code, I get this response:
nutritionalInformation/image/base64encoded } { status code: 413, headers {
Connection = "keep-alive";
"Content-Length" = 18;
"Content-Type" = "text/html; charset=utf-8";
Date = "Sun, 04 Dec 2016 20:18:15 GMT";
Server = Cowboy;
Via = "1.1 vegur";
"X-Content-Type-Options" = nosniff;
"X-Powered-By" = Express;
} }
18 bytes
responseString = Optional("Payload Too Large\n")
So I did some research and I tried changing my app.js, which is my root file (the logic code is in api.js).
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({extended:true, limit: '50mb'}));
app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res) {
res.redirect('/api');
});
app.use('/api', require('./routes/api'));
However, when after this change, when I run the function in Swift, I now get a 500 error.
Any help resolving this issue would be greatly appreciated!
This problem occurs from the server side.
If your server is Node JS then make changes as:-
var express = require('express')
var bodyParser = require('body-parser')
var app = express()
app.use(bodyParser.json({ limit: '10mb' })) // change limit according to you.
for other server change limit.

fabric.js on node.js loadFromDatalessJSON moves objects when in a group

I am serializing a canvas on the client, and post it to a node.js server (ubuntu 14.10, with node.js version v0.10.34 and fabric 1.4.13).
On the client canvas, objects are in a group.
The problem is, the objects are moved when de-serialized on the server.
Client code:
$(function(){
fc= new fabric.Canvas('myCanvas');
fc.setBackgroundColor('white');
group = new fabric.Group([], { hasControls:false, hasBorders:true, top:-fc.getHeight(), left:-fc.getWidth(), width:2*fc.getWidth(), height:2*fc.getHeight(), hoverCursor:'default' });
fc.add(group);
// create a rectangle object
var rect = new fabric.Rect({
left: 150,
top: 100,
fill: 'red',
width: 20,
height: 20
});
// "add" rectangle onto canvas
group.add(rect);
var rect2 = new fabric.Rect({
left: 100,
top: 150,
fill: 'blue',
width: 20,
height: 20
});
group.add(rect2);
fc.renderAll();
$.post( window.location.origin+':8124/', {
width: group.getWidth(),
height: group.getHeight(),
data: encodeURI(JSON.stringify(fc.toDatalessJSON()))
}, function( data ) {}
);
});
Server code:
var fabric = require('fabric').fabric;
var express = require('express');
var app = express();
var fs = require('fs');
var PORT = 8124;
var bodyParser = require('body-parser')
app.use(bodyParser.json({ limit: '50mb'}) ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true,
limit: '50mb'
}));
app.post('/', function(req, res){
console.log('Post received');
if (req.body) {
res.writeHead(200, { 'Content-Type': 'image/png' });
var w=parseInt(req.body.width);
var h=parseInt(req.body.height);
var canvas = fabric.createCanvasForNode(w, h);
console.log(req.body.data);
out = fs.createWriteStream(__dirname + '/mindmap.png');
canvas.loadFromDatalessJSON(decodeURI(req.body.data), function() {
canvas.renderAll();
console.log(JSON.stringify(canvas.toDatalessJSON()));
var stream = canvas.createPNGStream();
stream.on('data', function(chunk) {
out.write(chunk);
console.log('writing chunk');
});
stream.on('end', function() {
out.end();
});
});
}
});
app.listen(PORT);
The console.log statement shows that the two rects are created (left:15, top-35) and (left:-35, top:15) respectively.
On the client, top/left object properties are relative to center of the group.
This is why I create the group with -fc.getWidth and-fc.getHeight left and top respectively. This works fine on the client.
Maybe this is not the case on the server?
EDIT: this seems to be an issue with loadFromDatalessJSON, at least on node.
Running the following code on the node server shows that top/left properties of the rectangles are wrong after serializing the first canvas and deserializing into the second one:
var fabric = require('fabric').fabric;
var fs = require('fs');
var canvas = fabric.createCanvasForNode(200, 200);
canvas.setBackgroundColor('white');
var out = fs.createWriteStream(__dirname + '/mindmap.png');
var group = new fabric.Group([], { top:-200, left:-200, width:400, height:400});
canvas.add(group);
var rect = new fabric.Rect({
left:150,
top:100,
fill:'red',
width:20,
height:20
});
group.add(rect);
var rect2 = new fabric.Rect({
left:100,
top:150,
fill:'blue',
width:20,
height:20
});
group.add(rect2);
canvas.renderAll();
console.log(JSON.stringify(canvas.toDatalessJSON()));
var canvas2 = fabric.createCanvasForNode(200, 200);
canvas2.loadFromDatalessJSON(canvas.toDatalessJSON());
canvas2.renderAll();
var stream = canvas2.createPNGStream();
stream.on('data', function(chunk) {
out.write(chunk);
console.log('writing chunk');
});
stream.on('end', function() {
out.end();
console.log('png image generated');
});
console.log(JSON.stringify(canvas2.toDatalessJSON()));
Next step is to run similar code on the client and see if the problem exists as well.
EDIT2: the same problem occurs on the client, and with toJSON as well instead of toDatalessJSON. Can someone help? Is this a known issue with groups serialization/deserialization? Is there a workaround?
Thanks
After searching for similar issues, it looks like this is very similar to [#1159] [https://github.com/kangax/fabric.js/issues/1159]
But this issue is supposed to be fixed, and I don't have any transform on my canvas ...
I had this problem and fixed it by installing the latest version from the github releases section: https://github.com/kangax/fabric.js/releases

Looking for a better way to do real time stock updates

I started with this project for my real time stock price update project.
This project works well when I am working with one or two stocks, but not when I want to update the price of hundreds of stocks at the same time. I'd like to know if I'm doing this the right way. Right now I fetch the data for all stocks in a for loop on the server, but the price update is very very slow. I'd like to know how to improve this.
I'd like to know how to update hundreds of stock prices each second, without affecting server performance.
I don't know if I should be sending the server a list of stocks I need from the client like: var ids = [ '', '', '', ... ], or if I can run those ids from the server itself.
Which is best: Stocks request from client to server, or from server to client?
Note: I will be using a different url to get stock price.
My server side code :
////
// CONFIGURATION SETTINGS
///
var PORT = 4000;
var FETCH_INTERVAL = 5000;
var PRETTY_PRINT_JSON = true;
///
// START OF APPLICATION
///
var express = require('express');
var http = require('http');
var io = require('socket.io');
var app = express();
var server = http.createServer(app);
var io = io.listen(server);
io.set('log level', 1);
server.listen(PORT);
var ticker = "";
app.get('/:ticker', function(req, res) {
ticker = req.params.ticker;
res.sendfile(__dirname + '/index.html');
});
io.sockets.on('connection', function(socket) {
var local_ticker = ticker;
ticker = "";
//Run the first time immediately
get_quote(socket, local_ticker);
//Every N seconds
var timer = setInterval(function() {
var ids = ['AAPL', '' , ..........100 stocks];
var l = ids.length;
for(var i=0; i<l; i++){
get_quote(socket, ids[i])
}
}, FETCH_INTERVAL);
socket.on('disconnect', function () {
clearInterval(timer);
});
});
function get_quote(p_socket, p_ticker) {
http.get({
host: 'www.google.com',
port: 80,
path: '/finance/info?client=ig&q=' + p_ticker
}, function(response) {
response.setEncoding('utf8');
var data = "";
response.on('data', function(chunk) {
data += chunk;
});
response.on('end', function() {
if(data.length > 0) {
try {
var data_object = JSON.parse(data.substring(3));
} catch(e) {
return;
}
var quote = {};
quote.ticker = data_object[0].t;
quote.exchange = data_object[0].e;
quote.price = data_object[0].l_cur;
quote.change = data_object[0].c;
quote.change_percent = data_object[0].cp;
quote.last_trade_time = data_object[0].lt;
quote.dividend = data_object[0].div;
quote.yield = data_object[0].yld;
p_socket.emit('quote', PRETTY_PRINT_JSON ? JSON.stringify(quote, true, '\t') : JSON.stringify(quote));
}
});
});
}
My client side code :
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.js"></script>
<script type="text/javascript" src="http://localhost:4000/socket.io/socket.io.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var socket = io.connect("http://localhost:4000");
socket.on('quote', function(data) {
var data = $("<pre>" + data + "</pre><hr />");
$("#quotes").append(data);
$("html, body").animate({ scrollTop: $(document).height() }, 100);
$(data).show("slide", { direction: "up" }, 250);
$(data).effect("highlight", {}, 1500);
});
});
</script>
<body>
<div id="quotes"></div>
</body>
I think that sending the desired ID's from the client side will make your application more flexible and easy to use. You can still write your server in a way that will be performant.
'For loops' will block Node's event loop. For async actions that need to iterate over an array I recommend:
https://github.com/caolan/async
Specifically 'async.each'
I haven't run your code but my gut tells me that my browser would not enjoy that much DOM manipulation all at once. I think that breaking the groups into smaller pieces would help. For instance:
Take your array of ID's and break it into 5. Then stagger the intervals of each.
var arr1 = [...]
var arr2 = [...]
var arr3 = [...]
var arr4 = [...]
var arr5 = [...]
setTimeout(doWorkOnArray(arr1), 4000)
setTimeout(doWorkOnArray(arr2), 3000)
setTimeout(doWorkOnArray(arr3), 2000)
setTimeout(doWorkOnArray(arr4), 1000)
setTimeout(doWorkOnArray(arr5), 0)
function doWorkOnArray(arr) {
setInterval(getData(arr), 5000)
}
Alternatively you could look at setting up a Master/Worker with something like Redis to queue the work. I think this would be the best performance. Check out:
https://github.com/Automattic/kue

Send PDF file from AngularJS to NodeJS

i need to send a PDF file from angularjs client to NodeJS service.
I did the angularjs service, and when i receive the file its a string like this:
%PDF-1.3
3 0 obj
<</Type /Page
/Parent 1 0 R
/Reso
How can i reconvert this string to PDF in NodeJS?
This is the client code:
var sendByEmail = function () {
$scope.generatingPdf = true;
$('#budget').show();
var pdf = new JsPDF('p', 'pt', 'letter');
var source = $('#budget')[0];
pdf.addHTML(source, 0, 0, function () {
var resultPdf = pdf.output();
BillService.sendByEmail("rbrlnx#gmail.com", resultPdf).then(function () {
});
$('#budget').hide();
});
};
var sendByEmail = function (email, file) {
var deferred = $q.defer();
var data = {
email: email,
file: file
};
BillService.sendByEmail(data, function (result) {
deferred.resolve(result);
}, function () {
deferred.reject();
});
return deferred.promise;
};
The server code controller its empty:
var sendByEmail = function (req, res, next) {
var file = req.body.file;
};
I experimented with this a while ago, and I came up with this. It's not production ready by a long shot maybe you find it useful. It's free of front end libraries (except Angular ofcourse), but assumes you're using Express 4x and body-parser.
The result:
In the browser:
On the server:
What you're seeing:
You're seeing a tiny node server, serving static index.html and angular files, and a POST route receiving a PDF in base64 as delivered by the HTML FileReader API, and saves it to disk.
Instead of saving to disk, you can send it as an email attachment. See for instance here or here for some info on that.
The example below assumes uploading a PDF by a user through a file input, but the idea is the same for all other ways of sending a document to your back end system. The most important thing is to send the pdf data as BASE64, because this is the format that most file writers and email packages use (as opposed to straight up binary for instance..). This also goes for images, documents etc.
How did I do that:
In your HTML:
<div pdfs>Your browser doesn't support File API.</div>
A directive called pdfs:
myApp.directive('pdfs', ['upload', function(upload) {
return {
replace: true,
scope: function() {
files = null;
},
template: '<input id="files" type="file">',
link: function(scope,element) {
element.bind('change', function(evt) {
scope.$apply(function() {
scope.files = evt.target.files;
});
});
},
controller: function($scope, $attrs) {
$scope.$watch('files', function(files) {
//upload.put(files)
if(typeof files !== 'undefined' && files.length > 0) {
for(var i = 0; i<files.length;i++) {
readFile(files[i])
}
}
}, true);
function readFile(file) {
var reader = new FileReader();
reader.addEventListener("loadend", function(evt) {
upload.post({name: file.name, data: reader.result})
})
if(reader.type = 'application/pdf') {
reader.readAsDataURL(file);
}
}
}
}
}]);
A tiny service:
myApp.service('upload', function($http) {
this.post = function(file) {
$http.post('/pdf', file);
}
});
And a node server:
var express = require('express');
var bodyParser = require('body-parser')
var fs = require("fs");
var app = express();
app.use(express.static('.'));
app.use( bodyParser.json({limit: '1mb'}) );
app.post('/pdf', function(req, res){
var name = req.body.name;
var pdf = req.body.data;
var pdf = pdf.replace('data:application/pdf;base64,', '');
res.send('received');
fs.writeFile(name, pdf, 'base64', function(err) {
console.log(err);
});
});
var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});

Resources