上次写删除文件夹的时候用到了fs模块,也集中用到了很多种路径,当时就想写一下,在Node中使用各种路径的问题,于是就简单写了一下,可以从 这里 获取demo源代码。

刚写Node的时候经常会遇到这种情况:比如项目入口是app.js,而app.js并不一定在根目录下,比如他在bin目录下,到了启动项目时,使用node /bin/app.js和进入bin文件夹直接node app.js总会有一个起不起来,都是些路径找不到的错误,究其原因就是启动应用时执行的目录不同了,不过为什么启动脚本的位置会有这么多影响呢,让我们来总结一下。

对比常用的几种路径

Node 中的文件路径大概有 __dirname,__filename,process.cwd(),./ 或者 ../。前三个都是绝对路径,为了便于比较,./ 和 ../ 我们通过 path.resolve('./')来转换为绝对路径。先看一个简单的例子。

假如我们有这样的文件结构:

javascript历偏文件夹 js文件夹路径_相对路径

在 server.js 里编写如下的代码:

var path = require('path');
console.log(__dirname);
console.log(__filename);
console.log(process.cwd());
console.log(path.resolve('./'));

在 path-test 目录下运行 node bin/server.js 得到的结果:

javascript历偏文件夹 js文件夹路径_nodejs 当前文件路径_02

进入 bin 目录下运行 node server.js 得到的结果:

javascript历偏文件夹 js文件夹路径_绝对路径_03

现在我们可以总结下这几个路径的意思:

__dirname: Nodejs的一个全局变量,获得当前执行文件所在目录的完整目录名

__filename: Nodejs的一个全局变量,获得当前执行文件的带有完整绝对路径的文件名

process.cwd():Nodejs的全局变量process的一个方法,返回当前进程的工作目录

./: 一般情况跟 process.cwd() 一样,返回 node 命令运行时所在的文件夹的绝对路径

注意__dirname得到的目录和命令执行所在的目录、__filename得到的文件名和参数指定的文件名都不一定相同,因为可能在一个文件中调用了另一个目录中的另一个文件。

更复杂的情况

我们把例子升级一下,在bin目录下新建一个test.js:

var fs = require('fs');
require('./server.js');
fs.readFile('./server.js', function (err, data){
if (err) return console.log(err);
console.log(data);
});

现在目录结构如下:

javascript历偏文件夹 js文件夹路径_绝对路径_04

我们这次先进入bin目录执行node test.js,得到的结果:

javascript历偏文件夹 js文件夹路径_文件名_05

可以看到是正常的,然后我们退出bin目录,在上一级执行node bin/test.js,得到结果:

javascript历偏文件夹 js文件夹路径_nodejs 当前文件路径_06

我们可以看到报错了,但是require是OK的,只是fs.readFile时路径出现了错误。从第一个例子我们可以知道,使用相对路径出现错误是预期之内的,因为在bin目录外执行时目录下已经没有server.js这个文件了,但是为什么在require中使用相对路径,就不受启动应用时执行命令目录不同的影响呢?实际上是require有自己的搜索机制,具体可以看require() 源码解读。

使用path模块处理文件路径

面对复杂的路径问题,path模块可以帮你规范化连接和解析路径,还可以用于绝对路径到对路径的转换、提取路径的组成部分及判断路径是否存在等。常用的两个命令:

path.join

path.join()方法可以连接任意多个路径字符串,只是简单的连接,不会看是否真的存在。

var path = require('path');
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')
// 连接后
'/foo/bar/baz/asdf'
path.resolve

path.resolve()方法可以将多个路径解析为一个规范化的绝对路径。其处理方式类似于对这些路径逐一进行cd操作,与cd操作不同的是,这引起路径可以是文件,并且可不必实际存在(resolve()方法不会利用底层的文件系统判断路径是否存在,而只是进行路径字符串操作)。1path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile')

其处理方式类型于

cd foo/bar
cd /tmp/file/
cd ..
cd a/../subfile
pwd

如果解析的不是绝对路径,path.resolve()会将当前工作目录(非进程工作目录)加到解析结果的前面。例如:

path.resolve('/foo/bar', './baz')

// 输出结果为

'/foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/')

// 输出结果为

'/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif')

// 当前的工作路径是 /Users/tyb/workspace/NodeJs/path-test/bin,则输出结果为

'/Users/tyb/workspace/NodeJs/path-test/bin/wwwroot/static_files/gif/image.gif'

两者区别join只是把各个path片段连接在一起, resolve会把‘/’当成根目录1

2path.join('/a', '/b') // 返回 '/a/b'

path.resolve('/a', '/b') // 返回 '/b'join直接拼接字段,resolve解析路径,如果解析的不是绝对路径,会在前面增加当前文件所在的目录1

2path.join("a", "b1", "..", "b2") // 返回 'a/b2'

path.resolve("a", "b1", "..", "b2") // 返回 '/Users/tyb/workspace/NodeJs/path-test/bin/a/b2'

相同之处都会规范化路径

都不会去验证路径是否真的存在

总结一下

最好只在 require() 时才使用相对路径./或者../的写法,其他地方一律配合path`模块使用绝对路径。