2011-08-12 130 views
17

我在node.js中遇到了這個代碼的問題。我想遞歸遍歷一個目錄樹,並將回調action應用到樹中的每個文件。這是我的時刻代碼:使用Node.js遍歷目錄

var fs = require("fs"); 

// General function 
var dive = function (dir, action) { 
    // Assert that it's a function 
    if (typeof action !== "function") 
    action = function (error, file) { }; 

    // Read the directory 
    fs.readdir(dir, function (err, list) { 
    // Return the error if something went wrong 
    if (err) 
     return action(err); 

    // For every file in the list 
    list.forEach(function (file) { 
     // Full path of that file 
     path = dir + "/" + file; 
     // Get the file's stats 
     fs.stat(path, function (err, stat) { 
     console.log(stat); 
     // If the file is a directory 
     if (stat && stat.isDirectory()) 
      // Dive into the directory 
      dive(path, action); 
     else 
      // Call the action 
      action(null, path); 
     }); 
    }); 
    }); 
}; 

的問題是,在每個循環統計經由可變path呼籲每個文件。當回調被調用時,path已經有另一個值,因此它dive s到錯誤的目錄或調用action錯誤的文件。

也許這個問題很容易通過使用fs.statSync得到解決,但這不是我想要的解決方案,因爲它阻止了這個過程。

回答

14

var path = dir + "/" + file;

你忘了做path一個局部變量。現在它不會在循環後面改變。

+2

最後一個[npm包](http://search.npmjs.org/#/dive) – pvorb

+5

這將通過在文件頂部放置'「use strict」;'來實現。 – Domenic

+2

對於跨平臺兼容性,請使用節點的[path.join](http://nodejs.org/api/path.html#path_path_join_path1_path2)函數,而不是附加原始的'「/」'。 –

-1
function loop() { 
    var item = list.shift(); 
    if (item) { 
     // content of the loop 
     functionWithCallback(loop); 
    } else { 
     // after the loop has ended 
     whatever(); 
    } 
} 
+0

在'functionWithCallback(loop);'中,你不希望我做什麼。你能將你的解決方案應用於我的例子嗎? – pvorb

+0

對於列表中的每個元素,您都希望執行某項操作,然後該函數會執行該操作,然後調用回調函數。我將編輯tomorow使其更接近您的代碼。 – xavierm02

7

不知道我是否真的應該張貼此作爲一個答案,但爲了您的方便和其他用戶,這裏是OP的一個重寫的版本,這可能證明是有用的。它提供:

  • 更好的錯誤管理支持
  • 全局完成回調勘探完成

的代碼時,被稱爲:

/** 
* dir: path to the directory to explore 
* action(file, stat): called on each file or until an error occurs. file: path to the file. stat: stat of the file (retrived by fs.stat) 
* done(err): called one time when the process is complete. err is undifined is everything was ok. the error that stopped the process otherwise 
*/ 
var walk = function(dir, action, done) { 

    // this flag will indicate if an error occured (in this case we don't want to go on walking the tree) 
    var dead = false; 

    // this flag will store the number of pending async operations 
    var pending = 0; 

    var fail = function(err) { 
     if(!dead) { 
      dead = true; 
      done(err); 
     } 
    }; 

    var checkSuccess = function() { 
     if(!dead && pending == 0) { 
      done(); 
     } 
    }; 

    var performAction = function(file, stat) { 
     if(!dead) { 
      try { 
       action(file, stat); 
      } 
      catch(error) { 
       fail(error); 
      } 
     } 
    }; 

    // this function will recursively explore one directory in the context defined by the variables above 
    var dive = function(dir) { 
     pending++; // async operation starting after this line 
     fs.readdir(dir, function(err, list) { 
      if(!dead) { // if we are already dead, we don't do anything 
       if (err) { 
        fail(err); // if an error occured, let's fail 
       } 
       else { // iterate over the files 
        list.forEach(function(file) { 
         if(!dead) { // if we are already dead, we don't do anything 
          var path = dir + "/" + file; 
          pending++; // async operation starting after this line 
          fs.stat(path, function(err, stat) { 
           if(!dead) { // if we are already dead, we don't do anything 
            if (err) { 
             fail(err); // if an error occured, let's fail 
            } 
            else { 
             if (stat && stat.isDirectory()) { 
              dive(path); // it's a directory, let's explore recursively 
             } 
             else { 
              performAction(path, stat); // it's not a directory, just perform the action 
             } 
             pending--; checkSuccess(); // async operation complete 
            } 
           } 
          }); 
         } 
        }); 
        pending--; checkSuccess(); // async operation complete 
       } 
      } 
     }); 
    }; 

    // start exploration 
    dive(dir); 
}; 
+0

這太棒了!一個小的優化:在第一個待處理之後,您不需要checkSuccess。除非你已經完成了你目前所在的目錄,否則你永遠不會處於待定== 0狀態。 –

8

使用node-dir這一點。因爲你需要一個單獨的目錄和文件操作,我會給你2個簡單的迭代器,使用node-dir。

異步迭代目錄及其子目錄的文件並將文件路徑數組傳遞給回調函數。

var dir = require('node-dir'); 

dir.files(__dirname, function(err, files) { 
    if (err) throw err; 
    console.log(files); 
    //we have an array of files now, so now we'll iterate that array 
    files.forEach(function(filepath) { 
    actionOnFile(null, filepath); 
    }) 
}); 

異步迭代一個目錄及其子目錄的子目錄和傳遞的目錄路徑的陣列爲回調。

var dir = require('node-dir'); 

dir.subdirs(__dirname, function(err, subdirs) { 
    if (err) throw err; 
    console.log(subdirs); 
    //we have an array of subdirs now, so now we'll iterate that array 
    subdirs.forEach(function(filepath) { 
    actionOnDir(null, filepath); 
    }) 
}); 
7

另一種合適的文庫是filehound。它支持文件過濾(如果需要),回調和承諾。

例如:

const Filehound = require('filehound'); 

function action(file) { 
    console.log(`process ${file}`) 
} 

Filehound.create() 
.find((err, files) => { 
    if (err) { 
     return console.error(`error: ${err}`); 
    } 

    files.forEach(action); 
}); 

該庫是有據可查的,並提供了常見的用例的例子不勝枚舉。 https://github.com/nspragg/filehound

聲明:我是作者。