最近在添加网站的一些功能,本以为半个小时可以搞定的事情,却花了我好多时间。因此,决定顺便梳理一下。
需求:给定一个文件夹地址,把该文件夹以下的目录和文件递归的以文本的方式显示在网页上。
这个代码,如果用传统的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
更多 [ 技术 ] 文章