Node JS遍历文件夹
赖永炫   Tue Oct 11 2016 21:57:49 GMT+0800 (中国标准时间) [ 技术 ]     浏览次数:3320
版权声明: 本文发自http://mocom.xmu.edu.cn,为 赖永炫 老师的个人博文,文章仅代表个人观点。无需授权即可转载,转载时请务必注明作者。

最近在添加网站的一些功能,本以为半个小时可以搞定的事情,却花了我好多时间。因此,决定顺便梳理一下。

需求:给定一个文件夹地址,把该文件夹以下的目录和文件递归的以文本的方式显示在网页上。

这个代码,如果用传统的java或者C#语言来做,大概是非常轻松的事情。随手可以从网上可以摘录一个代码。

public   static   void   ListFiles(FileSystemInfo   info, ArrayList array) 
  { 
    if(!info.Exists)   
              return;
      DirectoryInfo   dir   =   info   as   DirectoryInfo; 
      //不是目录 
    if(dir   ==   null)   
              return;
         array.Add(dir.Name);
    FileSystemInfo   []   files   =   dir.GetFileSystemInfos(); 
    for(int   i   =   0;   i   <   files.Length;   i++) 
    { 
      FileInfo   file   =   files[i]   as   FileInfo; 
      //是文件 
      if(file   !=   null) 
        array.Add(file.FullName   +   "\t "   +   file.Length); 
      //对于子目录,进行递归调用 
      else 
        ListFiles(files[i]);
    } 
  } 
}

这里有关键语句FileSystemInfo [] files = dir.GetFileSystemInfos();获取文件夹下的所有目录和文件,随后判断是否是文件夹。如果是,继续递归,否则的话,加入到数字array中。几乎对js的回调非常习惯了,于是随手从百度上找找到了一个与dir.GetFileSystemInfos对应的函数:

fs.readdir(path[, options], callback)

The callback gets two arguments (err, files) where filesis an array of the names of the files in the directory excluding '.' and '..'.

但是,发现这个用这个回调函数,也来个递归,似乎比较难以下手。callback已经是回调了,当对files也进行循环判断,如果遇到的是文件夹,那怎么进行递归呢?(大家先想想)

var fs = require('fs');

/*

递归处理文件,文件夹

path 路径
floor 层数
handleFile 文件,文件夹处理函数

*/

function walk(path, floor, handleFile) {
    handleFile(path, floor);
    floor++;
    fs.readdir(path, function(err, files) {
        if (err) {
            console.log('read dir error');
        } else {
            files.forEach(function(item) {
                var tmpPath = path + '/' + item;
                fs.stat(tmpPath, function(err1, stats) {
                    if (err1) {
                        console.log('stat error');
                    } else {
                        if (stats.isDirectory()) {
                            walk(tmpPath, floor, handleFile);
                        } else {
                            handleFile(tmpPath, floor);
                        }
                    }
                })
            });

        }
    });
}

exports.walk = walk;

这里的关键还是回调,即在递归函数walk里,嵌入一个handleFile的回调函数,遇到文件的时候调用它;遇到文件夹的时候,还用原来的递归函数walk。具体的用法如下:

var dirWalker = require('./dirWalker');
var fs = require('fs');

var array=new Array();
function handleFile(path, floor) {
    var blankStr = '';
    for (var i = 0; i < floor; i++) {
        blankStr += '    ';
    }
    fs.stat(path, function(err1, stats) {
        if (err1) {
            console.log('stat error');
        } else {
            if (stats.isDirectory()) {
                array.push('+' + blankStr + path);
            } else {
                array.push('-' + blankStr + path);
            }
        }
    })
}

dirWalker.walk('/Users/MacBackup', 0, handleFile);

我发现,还有一个看上去稍微复杂点的写法,感兴趣的可以继续研究:

var fs = require('fs');
var path = require('path');


var array=new Array();
function travel(dir, visit, finish) {
    fs.readdir(dir, function (err, files) {
        (
            function next(i) { //定义一个函数next,并同时调用 next(0);
                if (i < files.length) {
                    var pathname = path.join(dir, files[i]);
                    fs.stat(pathname, function (err, stats) {
                        if (stats.isDirectory()) {
                            travel(pathname, visit, function () {
                                next(i + 1);
                            });
                        } else {
                            visit(pathname, function () {
                                next(i + 1); //处理一个文件,才继续调用下一个
                            });
                        }
                    });
                } else { //顶层的文件/子目录全部处理完毕,调用finish
                    finish && finish(array);
                }`
            }(0)
        );
    });
}

travel("/Users/MacBackup",
    function addPathToArray(pathname,callback){
        array.push(pathname);
        callback();
    },
    function finish(){
        for(var i=0;i<array.length;i++){
            console.log(i+": "+array[i]);
        }
    });

这里,travel函数的visit和finish都是现场定义的回调函数。(visit=addPathToArray,finish=finish)。对于fs.readdir返回来的files中的每个文件逐一判断。程序控制得很周全:对于1个具体的文件files[i],如果是文件夹,则继续调用travel函数;如果是文件,则调用visit。无论种类如何,都必须等到处理完file[i]后,才继续处理files[i+1]。这种一环扣一环的执行顺序,依靠的是travel和visit的回调函数next。直到所有的顶层的files文件都列举和访问完了,则才开始调用finish函数。这样的精心回调和程序流控制,的确很费脑^_^

花了九牛二虎之力理解了回调的写法。其实回过头来仔细看nodejs的fs模块,它为所有的文件操作,都提供了非回调的版本1。也为readdir提供了一个非回调的版本:

fs.readdirSync(path[, options])

此时写个与c#一样的函数,轻而易举:

function travelPath(path1, fileArray) {
    var array = fs.readdirSync(path1)   
    for (var i = 0; i < array.length; i++) {
        var s = path1 + "/" + array[i];
        fileArray.push(s);
        var stats = fs.statSync(s);
        if (stats.isDirectory()) {
            travelPath(s, fileArray);
        }
    }
}


自动标签  : Node   文件夹   文件   递归   file   函数   调用   处理   array   visit   可以   继续   callback    

更多 [ 技术 ] 文章

请先 登录, 查看相关评论.