nodejs / express - 将stdout立即传送给客户端

时间:2014-02-21 12:43:42

标签: node.js express spawn child-process

我催生了以下孩子:var spw = spawn('ping', ['-n','10', '127.0.0.1'])我希望在客户端(浏览器逐个收到ping结果,而不是一个整体。

到目前为止,我试过这个:

app.get('/path', function(req, res) {
   ...
   spw.stdout.on('data', function (data) {
      var str = data.toString();
      res.write(str + "\n");
   });
   ...
}

那:

...
spw.stdout.pipe(res);
...

在这两种情况下,浏览器都会等待10个ping完成,然后将结果作为一个整体打印出来。我想一个接一个地拿到它们,如何实现呢?

(客户只是调用.../path并调用console.logs结果)


编辑:虽然我确实认为必须使用websockets来实现这一点,但我只想知道是否还有其他方法。我看到了几个令人困惑的SO answers和博客文章(在this帖子中,在第一步OP将日志流式传输到浏览器),这没有帮助,因此我决定寻求赏金以获得一些关注

3 个答案:

答案 0 :(得分:26)

这是使用SSE(服务器发送事件)的完整示例。这适用于Firefox,也可能适用于Chrome:

var cp = require("child_process"),
         express = require("express"),
         app = express();

app.configure(function(){
    app.use(express.static(__dirname));
});


app.get('/msg', function(req, res){
    res.writeHead(200, { "Content-Type": "text/event-stream",
                         "Cache-control": "no-cache" });

    var spw = cp.spawn('ping', ['-c', '100', '127.0.0.1']),
    str = "";

    spw.stdout.on('data', function (data) {
        str += data.toString();

        // just so we can see the server is doing something
        console.log("data");

        // Flush out line by line.
        var lines = str.split("\n");
        for(var i in lines) {
            if(i == lines.length - 1) {
                str = lines[i];
            } else{
                // Note: The double-newline is *required*
                res.write('data: ' + lines[i] + "\n\n");
            }
        }
    });

    spw.on('close', function (code) {
        res.end(str);
    });

    spw.stderr.on('data', function (data) {
        res.end('stderr: ' + data);
    });
});

app.listen(4000);

客户端HTML:

<!DOCTYPE Html>
<html> 
<body>
   <ul id="eventlist"> </ul>

   <script>              
    var eventList = document.getElementById("eventlist");
    var evtSource = new EventSource("http://localhost:4000/msg");

    var newElement = document.createElement("li");
    newElement.innerHTML = "Messages:";
    eventList.appendChild(newElement);


    evtSource.onmessage = function(e) {
        console.log("received event");
        console.log(e);
        var newElement = document.createElement("li");

        newElement.innerHTML = "message: " + e.data;
        eventList.appendChild(newElement);
    };      

    evtSource.onerror = function(e) {
        console.log("EventSource failed.");
    };

    console.log(evtSource);

    </script>

</body>
</html>

运行node index.js并将浏览器指向http://localhost:4000/client.html。 请注意,我必须使用“-c”选项而不是“-n”,因为我正在运行OS X.

答案 1 :(得分:2)

如果您使用的是谷歌浏览器,将内容类型更改为“text / event-stream”可以满足您的需求。

res.writeHead(200, { "Content-Type": "text/event-stream" });

请参阅我的要点以获取完整示例:https://gist.github.com/sfarthin/9139500

答案 2 :(得分:1)

使用标准HTTP请求/响应周期无法实现。基本上你要做的是制作一个“推”或“实时”服务器。这只能通过xhr-polling或websockets来实现。

代码示例1:

app.get('/path', function(req, res) {
   ...
   spw.stdout.on('data', function (data) {
      var str = data.toString();
      res.write(str + "\n");
   });
   ...
}

此代码从不发送结束信号,因此永远不会响应。如果要在该事件处理程序中添加对res.end()的调用,则只会获得第一个ping - 这是预期的行为,因为您在stdout的第一个数据块之后结束了响应流。

代码示例2:

spw.stdout.pipe(res);

这里stdout正在将数据包刷新到浏览器,但是在收到所有数据包之前,浏览器不会呈现数据块。因此,它等待10秒然后呈现整个stdout的原因。这种方法的主要好处是不会在发送之前缓冲内存中的响应 - 保持内存占用的轻量级。

相关问题