Show Node.js buffer in real time in a browser

4

I'm trying to make a program in Node.js that, after receiving a Unix-like command via POST , it executes it and displays in real time the output in the browser.

This is the code I'm using to execute the commands:

 exec(myCommand, function(err, stdout, stderr){
     sys.print('stdout: ' + stdout);
     sys.print('stderr: ' + stderr);
         if(err !== null) {
             console.log('EXECUTION ERROR: ' , err);
         }
 }

However, the output of stdout comes in the form of Buffer , so I'm not sure how I can make the browser show its content, as it increases.

My idea is that if, for example, I execute the apt-get update command, I will show what it is doing in real time, and not all the output when it has already finished.

It occurred to me that I could save the contents of the buffer in a text string repeatedly, and then have the browser, through JavaScript, check the updates frequently, but it seems like a bit of a sloppy solution, Not to mention that it is not as fast as I would like.

In conclusion, how can I do this as quickly and simply as possible?

    
asked by Hewbot 23.12.2015 в 20:05
source

2 answers

3

So that the exit will be shown as it goes out and not suddenly you should use spawn instead of using exec . I think it fits more to what you want to achieve. If you want to understand the difference between the two functions you can visit this link .

To be shown in real time you must use socket.io , it is much more reliable than using GET requests at intervals to get what the command is printing and you can use it as a transport to start executing them.

Here I put a demo that sends the commands from any client and the output is reflected in all the others that are connected. I send the command using the same socket but you can modify it and use POST if you want.

Since you do not specify if you can use other frameworks, use it using only node.js and socket.io.

var http = require('http'),
fs = require('fs'),
url = require('url'),
child = require('child_process');

var fileStream = fs.readFileSync('cmd.html'),
    myCommand = 'cmd.exe',
    myArgs = ['/?'];

var server = http.createServer(function (req, res) {
    if (url.parse(req.url).pathname === '/') {
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.end(fileStream);
    } else {
        res.writeHead(404, {'Content-Type': 'text/plain'});
        res.end('Not found');
    }
}).listen(3000);

function executeCommand(command, args) {
    var toExec = command || myCommand,
        theArgs = args ? args : (command ? [] : myArgs);

    var cmd = child.spawn(toExec, theArgs);
    cmd.stdout.on('data', function (data) {
        io.emit('output', data.toString());
    });

    cmd.stderr.on('data', function (data) {
         io.emit('output', data.toString());
    });

    cmd.on('error', function (err) {
        io.emit('output', err.message);
    });

    cmd.on('close', function (code) {
       io.emit('Process exited with code ' + code);
    });
 }

 var io = require('socket.io')(server);

 io.on('connection', function(socket) {
     socket.on('command', function(cmd) {
         var c = cmd.split(' ');
        executeCommand(c[0], c.slice(1));
    });
});

console.log('Server running at http://127.0.0.1:3000/');

And this is the cmd.html file sent to the client

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Live command</title>
</head>
<body>
    <div id="dashboard"
 style="width: 100%; height: 300px; overflow: auto; background-color: black; color: white; border: gray solid 3px">
    </div>
    <div style="margin-top: 30px">
        <label  for="command">Command:</label>
        <input id="command" type="text">
        <button id="send" type="button">Execute</button>
    </div>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        window.onload = function () {
            var socket = io('http://localhost:3000');
            var console = document.getElementById('dashboard');
            var button = document.getElementById('send');
            var command = document.getElementById('command');

            socket.on('output', function (data) {
                var newLine = document.createElement('p');
                newLine.innerHTML = data;
                console.appendChild(newLine);
            });

            button.addEventListener('click', function () {
                socket.emit('command', command.value);
            });
        };
    </script>
</body>
</html>
    
answered by 24.12.2015 / 17:16
source
2

The method that should be used is spawn instead of exec , since the second waits for the command to finish (and that's what you want to avoid).

The stdout (from child_process) is a readable stream (read) and the response (from http) is a writable stream, so what you are missing is a pipe (pipe, but in the console it is better known as | ).

The code would be something like:

var http = require('http');
var spawn = require('child_process').spawn;

var server = http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  spawn('cat', ['streamout.js']).stdout.pipe(res);
});

server.listen(3000, function() {
  console.log('server running on http://localhost:3000');
});

If in the middle you want to do some kind of processing, you will have to do it using these same streams: stdout.pipe(colorize).pipe(beautify).pipe(res) .

The two readings I can recommend are (both unfortunately in English): stream-handbook , Some Libraries I use to build web-apps both from Substack. In the second there are many libraries that can be used to modify the stream.

    
answered by 24.12.2015 в 16:09