Node.js函数未按顺序运行。错误:管道中未处理的流错误

时间:2019-01-31 21:20:45

标签: node.js json synchronization axios export-to-csv

我更新了功能以创建CSV文件,但是现在出现错误:

上载功能 内部/流/legacy.js:57       投掷者//管道中未处理的流错误。       ^

错误:ENOENT:没有此类文件或目录,请打开“ C:\ Users \ shiv \ WebstormProjects \ slackAPIProject \ billingData \ CSV \ 1548963844106output.csv”

var csvFilePath = '';
var JSONFilePath = '';
function sendBillingData(){
  var message = '';
  axios.get(url, {
    params: {
      token: myToken
    }
  }).then(function (response) {
    message = response.data;

    fields = billingDataFields;
    // saveFiles(message, fields, 'billingData/');
    saveFilesNew(message, fields, 'billingData/');
    var file = fs.createReadStream(__dirname + '/' + csvFilePath);   // <--make sure this path is correct
    console.log(__dirname + '/' + csvFilePath);
    uploadFile(file);
  })
      .catch(function (error) {
        console.log(error);
      });
} 

saveFilesNew函数为:

function saveFilesNew(message, options, folder){
  try {
    const passedData = message;

    var relevantData='';

    if (folder == 'accessLogs/'){
      const loginsJSON = message.logins;
      relevantData = loginsJSON;
      console.log(loginsJSON);
    }
    if(folder == 'billingData/'){
      relevantData = passedData.members;
      const profile = passedData.members[0].profile;
    }

    //Save JSON to the output folder
    var date = Date.now();
    var directoryPath = folder + 'JSON/' + date + "output";
    JSONFilePath = directoryPath + '.json';
    fs.writeFileSync(JSONFilePath, JSON.stringify(message, null, 4), function(err) {
      if (err) {
        console.log(err);
      }
    });

    //parse JSON onto the CSV
    const json2csvParser = new Json2csvParser({ fields });
    const csv = json2csvParser.parse(relevantData);
    // console.log(csv);

    //function to process the CSV onto the file
    var directoryPath = folder + 'CSV/' + date + "output";
    csvFilePath = directoryPath + '.csv';

    let data = [];
    let columns = {
      real_name: 'real_name',
      display_name: 'display_name',
      email: 'email',
      account_type: 'account_type'
    };

    var id = passedData.members[0].real_name;
    console.log(id);

    console.log("messageLength is" +Object.keys(message.members).length);

    for (var i = 0; i < Object.keys(message.members).length; i++) {
      console.log("value of i is" + i);
      var display_name = passedData.members[i].profile.display_name;
      var real_name = passedData.members[i].profile.real_name_normalized;
      var email = passedData.members[i].profile.email;
      var account_type = 'undefined';
      console.log("name: " + real_name);

      if(passedData.members[i].is_owner){
        account_type = 'Org Owner';
      }
      else if(passedData.members[i].is_admin){
        account_type = 'Org Admin';
      }
      else if(passedData.members[i].is_bot){
        account_type = 'Bot'
      }
      else account_type = 'User';

      data.push([real_name, display_name, email, account_type]);
    }

    console.log(data);

    stringify(data, { header: true, columns: columns }, (err, output) => {
      if (err) throw err;
      fs.writeFileSync(csvFilePath, output, function(err) {
        console.log(output);
        if (err) {
              console.log(err);
            }
        console.log('my.csv saved.');
      });
    });

  } catch (err) {
    console.error(err);
  }
}

上传文件功能为:

function uploadFile(file){
  console.log("In upload function");
  const form = new FormData();
  form.append('token', botToken);
  form.append('channels', 'testing');
  form.append('file', file);
  axios.post('https://slack.com/api/files.upload', form, {
    headers: form.getHeaders()
  }).then(function (response) {
    var serverMessage = response.data;
    console.log(serverMessage);
  });
}

所以我认为是由于节点试图在创建文件之前上载文件而引起的错误。我觉得这与Node.js的异步特性有关,但是我无法理解如何纠正代码。请让我知道如何纠正此问题,并同时提及对代码结构/设计的任何改进。 谢谢!

1 个答案:

答案 0 :(得分:1)

您不必等待提供给stringify的回调函数的执行,而这正是您创建文件的地方。 (假设此stringify函数确实接受了回调。)

使用回调(您可以使用promises和这些整洁的async / await控件使这个更简洁,但让我们在这里仅坚持使用回调),它应该更像是:

function sendBillingData() {
  ...
  // this callback we'll use to know when the file writing is done, and to get the file path
  saveFilesNew(message, fields, 'billingData/', function(err, csvFilePathArgument) {
    // this we will execute when saveFilesNew calls it, not when saveFilesNew returns, see below
    uploadFile(fs.createReadStream(__dirname + '/' + csvFilePathArgument))
  });
}

// let's name this callback... "callback".
function saveFilesNew(message, options, folder, callback) {
  ...
  var csvFilePath = ...; // local variable only instead of your global
  ...
  stringify(data, { header: true, columns: columns }, (err, output) => {
    if (err) throw err; // or return callbcack(err);
    fs.writeFile(csvFilePath , output, function(err) { // NOT writeFileSync, or no callback needed
      console.log(output);
      if (err) {
        console.log(err);
        // callback(err); may be a useful approach for error-handling at a higher level
      }
      console.log('my.csv saved.'); // yes, NOW the CSV is saved, not before this executes! Hence:
      callback(null, csvFilePath); // no error, clean process, pass the file path

    });
  });
  console.log("This line is executed before stringify's callback is called!");
  return; // implicitly, yes, yet still synchronous and that's why your version crashes
}

使用仅在预期事件发生时(文件已完成写,缓冲区/字符串已完成转换...)才调用的回调使JS可以在此期间继续执行代码。而且它确实会继续执行代码,因此当您需要异步代码中的数据时,需要在执行代码之前告诉JS您需要完成它。

此外,由于您可以在回叫时传递数据(这只是一个函数),因此在这里我可以避免依赖全局csvFilePath。使用更高级别的变量使事情变得单一,就像您无法将saveFilesNew转移到专用文件中一样,该文件中保留了文件相关功能的工具包。

最后,如果您的全局流程如下:

function aDayAtTheOffice() {
  sendBillingData();
  getCoffee();
}

那么您就无需在开始煮咖啡之前就等待计费数据处理完毕了。但是,如果老板告诉您在结算帐单数据之前无法喝咖啡,那么您的流程将如下所示:

function aDayAtTheOffice() {
  sendBillingData(function (err) {
    // if (err)  let's do nothing here: you wanted a coffee anyway, right?
    getCoffee();
  });
}

(请注意,具有潜在错误的回调是第一个arg,数据是第二个arg,这是惯例,没有强制性。)

恕我直言,您应该阅读scope(在已经callback的调用已经被忘记的时候可以访问参数saveFilesNew)和{{3} }。 ;)(很抱歉,可能不是最好的链接,但它们包含有意义的关键字,然后Google是您的好友,您的朋友,您的老大哥。)