2017-06-12 94 views
1

我有一个同步过程,我正在从C#迁移到nodejs,每天都会为某些文件检查一个目录。如果这些文件存在,则将它们添加到TAR文件中,并将该TAR写入其他目录。在使用forEach循环检查任何相关文件的同时,我正努力让我的进程在转到下一个函数之前等待循环完成,以创建TAR文件。nodejs - 等待fs.stat完成

我已经尝试使用async模块作为建议here并承诺建议here。没有太大的成功。

通过使用async模块,我希望暂停执行命令,以便在返回fileList数组之前完成循环。目前,我收到了TypeError: Cannot read property 'undefined' of undefined

我的问题:将async暂停执行,直到我的循环完成,如果是的话,我做错了什么?

感谢您的关注,请参阅下面的代码。

var fs = require('fs'), // access the file system. 
    tar = require('tar'), // archiving tools. 
    async = require('async'), // async tool to wait for the process's loop to finish. 
    moment = require('moment'), // date/time tools. 
    source = process.env.envA, // environment variable defining the source directory. 
    destination = process.env.envB, // environment variable defining the destination directory. 
    archiveName = process.env.envArc, // environment variable defining the static part of the TAR file's name. 
    searchParameter = process.env.env1, // environment variable defining a file search parameter. 
    date = moment().format('YYYYMMDD'); // Create a date object for file date comparison and the archive file name. 

// Change working directory the process is running in. 
process.chdir(source); 

// Read the files within that directory. 
fs.readdir(source, function (err, files) { 
    // If there is an error display that error. 
    if (err) { 
     console.log('>>> File System Error: ' + err); 
    } 

    // **** LOOP ENTRY POINT **** 
    // Loop through each file that is found, 
    // check it matches the search parameter and current date e.g. today's date. 
    CheckFiles(files, function (fileList) { 
     // If files are present create a new TAR file... 
     if (fileList > 0) { 
      console.log('>>> File detected. Starting archiveFiles process.'); 
      archiveFiles(fileList); 
     } else { // ...else exit the application. 
      console.log('>>> No file detected, terminating process.'); 
      //process.exit(0); 
     } 
    }); 
}); 

var CheckFiles = function (files, callback) { 
    console.log('>>> CheckFiles process starting.'); 

    var fileList = []; // Create an empty array to hold relevant file names. 

    // **** THE LOOP IN QUESTION **** 
    // Loop through each file in the source directory... 
    async.series(files.forEach(function (item) { 
     // ...if the current file's name matches the search parameter... 
     if (item.match(searchParameter)) { 
      // ...and it's modified property is equal to today... 
      fs.stat(item, function (err, stats) { 
       if (err) { 
        console.log('>>> File Attributes Error: ' + err); 
       } 
       var fileDate = moment(stats.mtime).format('YYYYMMDD'); 

       if (fileDate === date) { 
        // ...add to an array of file names. 
        fileList.push(item); 
        console.log('>>> Date match successful: ' + item); 
       } else { 
        console.log('>>> Date match not successful:' + item); 
       } 
      }); 
     } 
    }), callback(fileList)); // Once all the files have been examined, return the list of relevant files. 
    // **** END LOOP **** 

    console.log('>>> CheckFiles process finished.'); 
}; 

var archiveFiles = function (fileList) { 
    console.log('>>> Starting archiveFiles process.'); 

    if (fileList.length > 0) { 
     // Tar the files in the array to another directory. 
     tar.c({}, [fileList[0], fileList[1]]).pipe(fs.createWriteStream(destination + archiveName)); 
     // TODO Slack notification. 
     console.log('>>> TAR file written.'); 
    } 
}; 
+0

“承诺”呢? https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise – Lucas

+0

嗨@我是Blue Da Ba Dee我已经按照您的建议重构了使用promise的代码,但是@Cheloide指出:由于'fs.stat'的异步性质,我还没有完全修复它。 –

回答

0

Async是不必要的,使用Promises通过@的建议我是蓝多婆Dee和fs.statSync的建议通过@Cheloid满足我的要求。对于任何可能受益于此结果的人,请参阅下面的代码。

var fs = require('fs'), // access the file system. 
    tar = require('tar'), // archiving tools. 
    moment = require('moment'), // date/time tools. 
    source = process.env.envA, // environment variable defining the source directory. 
    destination = process.env.envB, // environment variable defining the destination directory. 
    archiveName = process.env.envArc, // environment variable defining the static part of the TAR file's name. 
    searchParameter = process.env.env1, // environment variable defining a file search parameter. 
    date = moment().format('YYYYMMDD'), // create a date object for file date comparison and the archive file name. 
    fileList = [], // create an empty array to hold relevant file names. 
    slack = require('./slack.js'); // import Slack notification functionality. 

// Change working directory the process is running in. 
process.chdir(source); 

// Read the files within that directory. 
fs.readdir(source, function (err, files) { 
    // If there is an error display that error. 
    if (err) console.log('>>> File System Error: ' + err); 

    // Loop through each file that is found... 
    checkFilesPromise(files).then(function (response) { 
     console.log('>>> File(s) detected. Starting archiveFilesPromise.'); 

     // Archive any relevant files. 
     archiveFilesPromise(fileList).then(function (response) { 
      console.log('>>> TAR file written.'); 

      // Send a Slack notification when complete. 
      slack('TAR file written.', 'good', response); 
     }, function (error) { 
      console.log('>>> archiveFilesPromise error: ' + error); 
      slack('archiveFilesPromise error:' + error, 'Warning', error); 
     }); 
    }, function (error) { 
     console.log('>>> CheckFilesPromise error ' + error); 
     slack('CheckFilesPromise error: ' + error, 'Warning', error); 
    }); 
}); 

var checkFilesPromise = function (files) { 
    return new Promise(function (resolve, reject) { 
     files.forEach(function (item) { 
      // ...check it matches the search parameter... 
      if (item.match(searchParameter)) { 
       var stats = fs.statSync(item); 
       var fileDate = moment(stats.mtime).format('YYYYMMDD'); 

       // ...and current date e.g. today's date. 
       if (fileDate === date) { 
        // Add file to an array of file names. 
        console.log('>>> Date match successful, pushing: ' + item); 
        fileList.push(item); 
        resolve('Success'); 
       } else { 
        reject('Failure'); 
       } 
      } 
     }); 
    }); 
}; 

var archiveFilesPromise = function (list) { 
    return new Promise(function (resolve, reject) { 

     if (list.length > 0) { 
      // Tar the files in the array to another directory. 
      tar.c({}, [list[0], list[1]]).pipe(fs.createWriteStream(destination + date + archiveName)); 
      resolve('Success'); 
     } else { 
      reject('Failure'); 
     } 
    }); 
}; 
0

你可以使用一个正常for循环,并在最后一次迭代调用回调函数。

var CheckFiles = function (files, callback) { 
    console.log('>>> CheckFiles process starting.'); 

    var fileList = []; // Create an empty array to hold relevant file names. 
    for (var i = 0, n = files.length; i < n; ++i) 
     // ...if the current file's name matches the search parameter... 
     if (item.match(searchParameter)) { 
      // ...and it's modified property is equal to today... 
      fs.stat(item, function (err, stats) { 
       if (err) { 
        console.log('>>> File Attributes Error: ' + err); 
       } 
       var fileDate = moment(stats.mtime).format('YYYYMMDD'); 

       if (fileDate === date) { 
        // ...add to an array of file names. 
        fileList.push(item); 
        console.log('>>> Date match successful: ' + item); 
       } else { 
        console.log('>>> Date match not successful:' + item); 
       } 
      }); 
     } 
    if (i === n + 1) { 
     callback(fileList); 
     console.log('>>> CheckFiles process finished.'); 
    } 
}; 

编辑:

使用递归的回调,我不知道如果这个代码可以工作,但我希望你的想法。

fs.stats是异步的,因此循环不会等待它...您可以使用回调函数来“等待”它。

var CheckFiles = function (files, callback) { 
    console.log('>>> CheckFiles process starting.'); 

    var arrIndex = 0; 
    var fileList = []; 

    recursiveCallback(fileList, callback); //callling our callback 

    function recursiveCallback(array, callback) { //recursive callback inside our function 

     var item = files[arrIndex++]; 

     if (item.match(searchParameter)) { 
      // ...and it's modified property is equal to today... 
      fs.stat(item, function (err, stats) { 
       if (err) { 
        console.log('>>> File Attributes Error: ' + err); 
       } 
       var fileDate = moment(stats.mtime).format('YYYYMMDD'); 

       if (fileDate === date) { 
        // ...add to an array of file names. 
        array.push(item); 
        console.log('>>> Date match successful: ' + item); 
       } else { 
        console.log('>>> Date match not successful:' + item); 
       } 
       if (files.length < arrIndex) //when last item, use the main callback to retrieve the array 
        callback(array); 
       else //when not last item , recursion 
        recursiveCallback(item, array, callback); 
      }); 
     } else if (files.length < arrIndex) //when last item, use the main callback to retrieve the array 
      callback(array); 
     else //when not last item , recursion 
      recursiveCallback(item, array, callback); 
    } 
} 
+0

嗨@Cheloide,谢谢你的回答。我已经尝试过您的建议,并重复了几次。但是我发现,在循环移到并执行回调之后,'fileList.push'操作正在执行。无论是那个还是我的'fileList.push'根本不工作? –

+0

@MattLindsay,我改变了我的答案。由于fs.stats的异步特性,我的最后一个答案将无法正常工作(也许fs.stats的同步版本将起作用)。 今天我还没有时间,否则我会尽快回复,我希望它有帮助。 – Cheloide

+0

嗨@Cheloid,谢谢你修改我现在有点接近。我发现这个过程在'array.push(item)'处退出。为了缓解这个问题,我在'recursiveCallback'构造函数后面添加了'array = []',它允许选择所有相关的文件。现在这个过程在最后一个过程中不存在,而实际上没有文件名。我已经尝试在正则表达式匹配'if'语句之前添加一个条件:'typeof item!== undefined',但那也没有奏效。任何想法都很受欢迎。 –