2011-10-24 74 views
9

我有一个函数可以根据发送给它的参数下载一个文件并将其保存在一个嵌套的目录结构中(例如:./somedir/a/b/c或./ somedir2 /一个/ d/b)中。我不能相信任何沿途的目录都已经创建好了,所以我需要让文件路径中的每个目录都被检查并创建(如果它不存在)。现在,我有一些完美适用于Node 0.4.x的代码,但是至少在Node 0.5.x的Windows版本(特别是在0.5.10上进行了测试)中出现了一些问题。为Node.js创建递归目录0.5.x

我对理解文件系统非常糟糕,所以如果有人能够弄清楚我怎样才能完成这项工作或者用其他类似的工具替代它,我将非常感激。目标是让代码在Unix和Windows以及Node 0.4.x和0.5.x上正常运行。

// automatically create directories if they do not exist at a path 
function mkdirs(_path, mode, callback) { 
    var dirs = _path.split("/"); 
    var walker = [dirs.shift()]; 

    var walk = function (ds, acc, m, cb) { 
    if (ds.length > 0) { 
     var d = ds.shift(); 
     acc.push(d); 
     var dir = acc.join("/"); 

     fs.stat(dir, function (err, stat) { 
     if (err) { 
      // file does not exist 
      if (err.errno == 2) { 
      fs.mkdir(dir, m, function (erro) { 
       if (erro && erro.errno != 17) { 
       terminal.error(erro, "Failed to make " + dir); 
       return cb(new Error("Failed to make " + dir + "\n" + erro)); 
       } else { 
       return walk(ds, acc, m, cb); 
       } 
      }); 
      } else { 
      return cb(err); 
      } 
     } else { 
      if (stat.isDirectory()) { 
      return walk(ds, acc, m, cb); 
      } else { 
      return cb(new Error("Failed to mkdir " + dir + ": File exists\n")); 
      } 
     } 
     }); 
    } else { 
     return cb(); 
    } 
    }; 
    return walk(dirs, walker, mode, callback); 
}; 

实例应用:

mkdirs('/path/to/file/directory/', 0777, function(err){ 

编辑:更新节点0.8.4(在CoffeeScript中):

# 
# Function mkdirs 
# Ensures all directories in a path exist by creating those that don't 
# @params 
# path:  string of the path to create (directories only, no files!) 
# mode:  the integer permission level 
# callback: the callback to be used when complete 
# @callback 
# an error object or false 
# 
mkdirs = (path, mode, callback) -> 
    tryDirectory = (dir, cb) -> 
    fs.stat dir, (err, stat) -> 
     if err #the file doesn't exist, try one stage earlier then create 
     if err.errno is 2 or err.errno is 32 or err.errno is 34 
      if dir.lastIndexOf("/") is dir.indexOf("/") #only slash remaining is initial slash 
      #should only be triggered when path is '/' in Unix, or 'C:/' in Windows 
      cb new Error("notfound") 
      else 
      tryDirectory dir.substr(0, dir.lastIndexOf("/")), (err) -> 
       if err #error, return 
       cb err 
       else #make this directory 
       fs.mkdir dir, mode, (error) -> 
        if error and error.errno isnt 17 
        cb new Error("failed") 
        else 
        cb() 
     else #unkown error 
      cb err 
     else 
     if stat.isDirectory() #directory exists, no need to check previous directories 
      cb() 
     else #file exists at location, cannot make folder 
      cb new Error("exists") 
    path = (if path.indexOf("\\") >= 0 then path.replace("\\", "/") else path) #change windows slashes to unix 
    path = path.substr(0, path.length - 1) if path.substr(path.length - 1) is "/" #remove trailing slash 
    tryDirectory path, callback 
+0

我已经使用不同分束逻辑确定如果操作系统是Windows'变种窗口= _path.indexOf( '\\')>尝试= 0;',但我仍然遇到ENOENT错误。从我所知道的情况来看,fs.stat在errno 32中返回一个错误,导致代码失败。 – geoffreak

+0

在做了一些更多的研究之后,似乎fs.stat有些问题,所以我提交了一个关于github的问题(https://github.com/joyent/node/issues/1927)。 – geoffreak

回答

6

看一看节点-FS(https://npmjs.org/package/node-fs)。

node-fs是原始nodejs fs库的扩展,提供了新的功能,例如递归目录创建。

+1

太好了!......这里唯一的答案就是让操作可以按我所能看到的完成。 – UpTheCreek

+0

是不是告诉我们如何用Node.fs来完成它? –

0

感觉挺傻回答我的问题,但我似乎有没有想到。只要确保使用基于__dirname的相对路径来确保跨平台兼容性。如果有人发现任何问题,请让我知道。我已经在Windows(0.5.10)和Mac(0.4.12)上测试过它。

// automatically create directories if they do not exist at a path 
function mkdirs(path, mode, callback){ 
    var path = path.indexOf('\\') >= 0 ? path.replace('\\', '/') : path;//change windows slashes to unix 
    if (path.substr(path.length - 1) == '/') { //remove trailing slash 
     path = path.substr(0, path.length - 1); 
    } 
    function tryDirectory(dir, cb){ 
     fs.stat(dir, function (err, stat) { 
      if (err) { //the file doesn't exist, try one stage earlier then create 
       if (err.errno == 2 || err.errno == 32) { 
        if (dir.lastIndexOf('/') == dir.indexOf('/')) {//only slash remaining is initial slash 
         //should only be triggered when path is '/' in Unix, or 'C:/' in Windows 
         cb(new Error('notfound')); 
        } 
        else { 
         tryDirectory(dir.substr(0, dir.lastIndexOf('/')), function(err){ 
          if (err) { //error, return 
           cb(err); 
          } 
          else { //make this directory 
           fs.mkdir(dir, mode, function (error) { 
            if (error && error.errno != 17) { 
             console.log("Failed to make " + dir); 
             return cb(new Error('failed')); 
            } else { 
             cb(); 
            } 
           }); 
          } 
         }); 
        } 
       } 
       else { //unkown error 
        console.log(util.inspect(err, true)); 
        cb(err); 
       } 
      } 
      else { 
       if (stat.isDirectory()) { //directory exists, no need to check previous directories 
        cb(); 
       } 
       else { //file exists at location, cannot make folder 
        return cb(new Error('exists')); 
       } 
      } 
     }); 
    } 
    tryDirectory(path, callback); 
}; 
1

基于geoffreak的同步版本。

感谢geoffreak。

function mkdirs(path, mode, callback){ 
    var path = path.indexOf('\\') >= 0 ? path.replace(/\\/g, '/') : path;//change windows slashes to unix 
    if (path.substr(path.length - 1) == '/') { //remove trailing slash 
     path = path.substr(0, path.length - 1); 
    } 
    console.log('path is:' + path); 

    function tryDirectory(dir, cb){ 
     console.log('path is:' + dir); 
     var stat ; 
     try { 
      stat = fs.statSync(dir) ; 

      // the file exist 
      if (stat.isDirectory()) { //directory exists, no need to check previous directories 
       cb(); 
      } 
      else { //file exists at location, cannot make folder 
       return cb(new Error('exists')); 
      } 

     } 
     catch(err) 
     { 
      if (err) { //the file doesn't exist, try one stage earlier then create 
       console.log('failed to get stat of ' + dir + ', errno is :' + err.errno); 
       if (err.errno == 2 || err.errno == 32 || err.errno == 34) { 

        //if (dir.lastIndexOf('/') == dir.indexOf('/')) {//only slash remaining is initial slash 
         //should only be triggered when path is '/' in Unix, or 'C:/' in Windows 
         //cb(new Error('notfound')); 
        //} 
        if (dir.length < 2) { 
         cb(new Error('invalid_path')); 
        } 
        else { 
         // try one stage earlier then create 
         tryDirectory(dir.substr(0, dir.lastIndexOf('/')), function(err){ 
          if (err) { //error, return 
           cb(err); 
          } 
          else { //make this directory 
           try { 
            fs.mkdirSync(dir, mode); 

            console.log('make dir ok, dir:' + dir); 
            cb();         
           } 
           catch (error) { 
            if (error && error.errno != 17) { 
             console.log("Failed to make " + dir); 
             return cb(new Error('failed')); 
            } 
           }         
          } 
         }); 
        } 
       } 
       else { //unkown error 
        console.log(util.inspect(err, true)); 
        cb(err); 
       } 
      } 

     } 

    } 
    tryDirectory(path, callback); 
}; 
12
function mkdir(path, root) { 

    var dirs = path.split('/'), dir = dirs.shift(), root = (root || '') + dir + '/'; 

    try { fs.mkdirSync(root); } 
    catch (e) { 
     //dir wasn't made, something went wrong 
     if(!fs.statSync(root).isDirectory()) throw new Error(e); 
    } 

    return !dirs.length || mkdir(dirs.join('/'), root); 
} 

用法:

var fs = require('fs'); 
mkdir('parent/child/grandchild'); 
+3

这对我很好,但是我必须用Node的跨平台路径分隔符常量更改硬编码的UNIX路径分隔符。而不是修改答案,我已经创建了它的要点https://gist.github.com/3960169 –

+0

啊,是的,忘了! – alzclarke

+0

不错,但令人遗憾的是它是同步的 – UpTheCreek

5

我有一个网站,该网站允许用户上传他们的照片到自己的文件夹中。我无法找到一个好的开源模块来递归创建目录,所以我实现了一个。 https://github.com/samxxu/ensureDir,其可以通过

$ npm install ensureDir 

安装下面是完整的源代码:

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

/** 
* ensure a directory exists, create it recursively if not. 
* 
* @param dir The directory you want to ensure it exists 
* @param mode Refer to fs.mkdir() 
* @param callback 
*/ 
module.exports = function ensureDir(dir, mode, callback) { 
    if (mode && typeof mode === 'function') { 
    callback = mode; 
    mode = null; 
    } 

    mode = mode || 0777 & (~process.umask()); 

    callback = callback || function() { 
    }; 

    _ensureDir(dir, mode, callback); 
} 

function _ensureDir(dir, mode, callback) { 
    var existsFunction = fs.exists || path.exists; 

    existsFunction(dir, function (exists) { 
    if (exists) return callback(null); 

    var current = path.resolve(dir); 
    var parent = path.dirname(current); 

    _ensureDir(parent, mode, function (err) { 
     if (err) return callback(err); 

     fs.mkdir(current, mode, function (err) { 
     if (err) return callback(err); 
     callback(); 
     }); 
    }); 
    }); 
} 
+0

似乎是最高效的异步版本。它自上而下地工作,以便从下往上不存在不必要的存在呼叫。 (即:进行最少量的系统调用)。 – Aktau

0

另外这个工程。迭代的,非递归的,算法。

```

function mkdirs(path) { 
    var dirs = path.split('/'); 
    var prevDir = dirs.splice(0,1)+"/"; 
    while(dirs.length > 0) { 
     var curDir = prevDir + dirs.splice(0,1); 
     if (! fs.existsSync(curDir)) { 
      fs.mkdirSync(curDir); 
     } 
     prevDir = curDir + '/'; 
    } 
} 

```

0

必须有一个非常小的版本,本作的这是关于一样漂亮,因为我可以把它部署服务器。

function mkdirs(file){ 
    var dirs, paths=[]; 
    dirs = file.split('/'); 
    while(dirs.length>1){ 
     paths.push(dirs.shift()); 
     var tmp = paths.join('/'); 
     if(!fs.existsSync('/'+tmp)) fs.mkdirSync('/'+tmp); 
    } 
} 
0

# npm install -g node-fs

CoffeeScript的代码

String::dirReAdd = -> 
    require('node-fs').mkdirSync(@.toString(), 0o777, true) 
    @ 

# './tmp2/example_sync/first/second/third/fourth/fifth/'.dirReAdd().pr()