node.js等待文件操作完成

时间:2014-08-23 23:04:07

标签: node.js web-services asynchronous

我是node.js的新手,我需要编写一个获取数据并将其写入文件的Web服务器。然后该文件用于外部程序。

我的问题是node.js在数据完全写入文件之前执行外部程序。如何在执行外部程序之前等待它完成

var http = require('http')
var map = require('through2-map')
var fs = require('fs')
var str = ""
var sh = require('execSync');
var tmp_filename = ""

    function random (low, high) {
    return Math.random() * (high - low) + low;
}

function execute_program(){
  var command = 'ruby ' + __dirname +  '/check_content.rb --file '+ tmp_filename
  var result = sh.exec(command);
  console.log('return code ' + result.code);
  console.log('stdout + stderr ' + result.stdout);
  return result.stdout
}

function write_to_file (chunk){
  str = chunk.toString();
  fs.appendFile(tmp_filename, str, function (err){
      if (err) throw err;
      console.log('The "data to append" was appended to file!');    
    }
  ) 
}

var server = http.createServer( function(req, res){
    tmp_filename = '/tmp/tmp_template_' + random(1000, 99999) + '.template'
    res.writeHead(200, {'Content-Type': 'text/plain'});
    message = req.pipe(map(function (chunk, res) {
      write_to_file(chunk);
    }))
    var out = execute_program();
    res.write(out)
    message.pipe(res)
  }
)

server.listen(8000)

2 个答案:

答案 0 :(得分:0)

注意:我不确定您使用res.write(out)然后message.pipe(res)尝试了什么。这会将ruby脚本的输出写入响应,然后看起来你在此之后管理请求体。实际上,message.pipe(res)行无法正常工作,因为您将尝试从可写流中进行管道传输,而您无法做到这一点。 为了这个答案的目的,我假设你不想要message.pipe(res)行;如果我错了,请纠正我。

简单/快速回答

最简单的答案是在end上监听req事件,然后在事件触发时执行您的程序等:

var server = http.createServer( function(req, res){
    tmp_filename = '/tmp/tmp_template_' + random(1000, 99999) + '.template'
    res.writeHead(200, {'Content-Type': 'text/plain'});
    req.on('end', function() {
        var out = execute_program();
        res.write(out);
        res.end();
    });
    req.pipe(map(function (chunk, res) {
        write_to_file(chunk);
    }));
  }
)

优化:fs.createWriteStream()

您也可以使用fs.createWriteStream()而不是使用fs.appendFile()附加数据块。那看起来像是:

var server = http.createServer( function(req, res){
    tmp_filename = '/tmp/tmp_template_' + random(1000, 99999) + '.template'
    res.writeHead(200, {'Content-Type': 'text/plain'});
    req.on('end', function() {
        var out = execute_program();
        res.write(out);
        res.end();
    });
    req.pipe(fs.createWriteStream(tmp_filename));
  }
)

有了这些功能,您就可以删除整个write_to_file功能。

如果您碰巧为临时文件获取相同的随机数,那么这也可以防止您遇到的问题;使用当前代码,您将附加到现有文件,这可能不是您想要的。如果由于某种原因你想要在这种情况下附加到文件,你可以将a标记(用于追加)传递给fs.createWriteStreamfs.createWriteStream(tmp_filename, {flags: 'a'});

答案 1 :(得分:0)

感谢Mike S. message.pipe(res)和res.write(out)是多余的...但我做了一些研究并使用了async.waterfall包,以下内容对我有用。但会采取你的建议进行优化

var http = require('http');
var map = require('through2-map');
var fs = require('fs');
var str = "";
var sh = require('execSync');
var async = require('async');
var tmp_filename = "";

function random (low, high) {
     return Math.random() * (high - low) + low;
}

function write_to_file (chunk){
  str = chunk.toString();
  fs.appendFile(tmp_filename, str, function (err){
      if (err) throw err;
      console.log('The "data to append" was appended to file!');    
    }
  ) 
}

function execute_program(){
  var command = 'ruby ' + __dirname +  '/check_content --file ' +  tmp_filename
  var result = sh.exec(command);
  console.log('return code ' + result.code);
  console.log('stdout + stderr ' + result.stdout);
  return result.stdout
}

async.waterfall([
    function(callback){
      var server = http.createServer(function(req, res){
          callback(null, req, res)
        }
      ).listen(8000);      
    },
    function(req, res, callback){
      tmp_filename = '/tmp/tmp_template_' + random(1000, 99999) + '.template'
      var message = req.pipe(map(function (chunk) {
        write_to_file(chunk);
      }))
      setTimeout(function () {
        callback(null, req, res, message);
      }, 666);
    },
    function(req, res, message, callback){
      var out = execute_program()
      res.write(out)
      message.pipe(res)
      callback(null, 'done');
    }
  ])