Node.js 如何检查路径是文件还是目录

578

我似乎找不到任何解释如何做到这一点的搜索结果。

我想要做的就是能够知道给定的路径是文件还是目录(文件夹)。


请注意,还有符号链接,可以链接到文件、链接到目录或者是损坏的。除了文件、目录和符号链接之外,还有其他类型的路径。因此,您必须小心,不要仅检查“目录”,并假设其他所有内容都是“文件”等。您还必须考虑是否希望透明地跟随符号链接。一个需要注意的问题是,scandir()返回的Direntstat()返回的Stat都有isFile()isDirectory()方法,但前者不会跟随符号链接,而后者会跟随。 - hippietrail
9个回答

898

以下内容应该能告诉你。来自文档

fs.lstatSync(path_string).isDirectory() 

从fs.stat()和fs.lstat()返回的对象属于此类型。

stats.isFile()
stats.isDirectory()
stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink() // (only valid with fs.lstat())
stats.isFIFO()
stats.isSocket()

注意:

上面的解决方案在文件或目录不存在时将抛出Error

如果您想采用truefalse方式,请尝试下面评论中Joseph提到的fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory();方法。


20
请注意,如果您关心应用程序的总体性能,异步版本通常更为合适。 - AlexMA
49
请记住,如果目录或文件不存在,则会返回错误。 - Ethan Mick
28
让我们翻译这段代码: let isDirExists = fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory();这行代码的功能是检查指定路径是否存在,并判断该路径是否为目录。如果路径存在且为目录,则变量isDirExists将被设置为true。需要注意的是,这行代码使用了Node.js提供的fs模块来操作文件系统。其中,existsSync方法用于检查路径是否存在,而lstatSync方法则用于获取路径的状态信息,包括路径是否为目录。 - Jossef Harush Kadouri
2
我觉得很奇怪,当他们第一次制作lstat时,为什么没有在其中包含exists()函数呢?我猜这就是为什么node_modules比黑洞还深的原因。 - Johncl
2
为什么每个人都在使用fs.lstat()?文档中说它总是false:如果<fs.Stats>对象是通过fs.lstat()获得的,那么这个方法[<fs.Stats>.isDirectory()]将始终返回false。 这是因为fs.lstat()返回有关符号链接本身而不是其解析路径的信息。 - snickle
显示剩余4条评论

98

更新:Node.js版本 >= 10

我们可以使用新的fs.promises API。

const fs = require('fs').promises;

(async() => {
    const stat = await fs.lstat('test.txt');
    console.log(stat.isFile());
})().catch(console.error)

任何Node.Js版本

以下是您如何异步检测路径是否为文件或目录的方法,这是在Node中推荐的方法。使用fs.lstat

const fs = require("fs");

let path = "/path/to/something";

fs.lstat(path, (err, stats) => {

    if(err)
        return console.log(err); //Handle error

    console.log(`Is file: ${stats.isFile()}`);
    console.log(`Is directory: ${stats.isDirectory()}`);
    console.log(`Is symbolic link: ${stats.isSymbolicLink()}`);
    console.log(`Is FIFO: ${stats.isFIFO()}`);
    console.log(`Is socket: ${stats.isSocket()}`);
    console.log(`Is character device: ${stats.isCharacterDevice()}`);
    console.log(`Is block device: ${stats.isBlockDevice()}`);
});

使用同步 API 时需要注意:

当使用同步形式时,任何异常都会立即抛出。您可以使用 try/catch 来处理异常或让它们上升到更高层次。

try{
     fs.lstatSync("/some/path").isDirectory()
}catch(e){
   // Handle error
   if(e.code == 'ENOENT'){
     //no such file or directory
     //do something
   }else {
     //do something else
   }
}

截至2020年3月,这仍被视为实验性的吗?我们在哪里可以查看?--糟糕,当我点击上面的链接时,我看到它现在是稳定的(这意味着不再是实验性的)。 - alfreema

35
认真地说,这个问题存在已经五年了,还没有一个漂亮的外观界面吗?
function isDir(path) {
    try {
        var stat = fs.lstatSync(path);
        return stat.isDirectory();
    } catch (e) {
        // lstatSync throws an error if path doesn't exist
        return false;
    }
}

当我提供可访问的目录 /tmp/ 时,出现 [Error: EACCES: permission denied, scandir '/tmp/snap.skype'] 的错误。 - Marinos An
@MarinosAn 我猜你没有那个文件的读取权限,所以它失败了。 - Clonkex

21

如果你需要在遍历目录时使用这个功能(因为我是这样发现这个问题的):

自 Node 10.10+ 版本起,fs.readdir 提供了一个 withFileTypes 选项,使其返回目录条目的 fs.Dirent 对象而不是字符串。目录对象具有 name 属性和有用的方法,如 isDirectoryisFile,因此您不需要显式调用 fs.lstat

import { promises as fs } from 'fs';

// ./my-dir has two subdirectories: dir-a, and dir-b

const dirEntries = await fs.readdir('./my-dir', { withFileTypes: true });

// let's filter all directories in ./my-dir

const onlyDirs = dirEntries.filter(de => de.isDirectory()).map(de => de.name);

// onlyDirs is now [ 'dir-a', 'dir-b' ]

16

根据您的需求,您可以在node的path模块上进行依赖。

您可能无法访问文件系统(例如,文件尚未创建),并且您可能希望避免访问文件系统,除非您确实需要额外的验证。如果您可以假设所检查的内容遵循.<extname>格式,请只查看名称。

显然,如果您正在寻找没有扩展名的文件,您需要访问文件系统以确保其存在。但在需要更复杂操作之前,请保持简单。

const path = require('path');

function isFile(pathItem) {
  return !!path.extname(pathItem);
}

3
显然这种方法并非在所有情况下都可行,但如果您能够进行所需的假设,那么它比其他答案要快得多、更容易实现。 - derpedy-doo
4
目录可以被命名为“folder.txt”,这将表明它是一个文件;或者文件可能是没有扩展名的“LICENSE”。 - wow ow

3
这是我使用的一个函数。在此帖子中,没有人使用promisifyawait/async特性,所以我想分享一下。
const promisify = require('util').promisify;
const lstat = promisify(require('fs').lstat);

async function isDirectory (path) {
  try {
    return (await lstat(path)).isDirectory();
  }
  catch (e) {
    return false;
  }
}

注意:我不使用require('fs').promises;,因为它已经实验了一年,最好不要依赖它。

1
以上答案检查文件系统是否包含文件或目录的路径。但它不能确定给定的路径本身是文件还是目录。
答案是使用“/。”来识别基于目录的路径,如-->“/c/dos/run/。”<-- 尾随句点。
例如,尚未编写的目录或文件的路径。或来自不同计算机的路径。或同时存在具有相同名称的文件和目录的路径。
// /tmp/
// |- dozen.path
// |- dozen.path/.
//    |- eggs.txt
//
// "/tmp/dozen.path" !== "/tmp/dozen.path/"
//
// Very few fs allow this. But still. Don't trust the filesystem alone!

// Converts the non-standard "path-ends-in-slash" to the standard "path-is-identified-by current "." or previous ".." directory symbol.
function tryGetPath(pathItem) {
    const isPosix = pathItem.includes("/");
    if ((isPosix && pathItem.endsWith("/")) ||
        (!isPosix && pathItem.endsWith("\\"))) {
        pathItem = pathItem + ".";
    }
    return pathItem;
}
// If a path ends with a current directory identifier, it is a path! /c/dos/run/. and c:\dos\run\.
function isDirectory(pathItem) {
    const isPosix = pathItem.includes("/");
    if (pathItem === "." || pathItem ==- "..") {
        pathItem = (isPosix ? "./" : ".\\") + pathItem;
    }
    return (isPosix ? pathItem.endsWith("/.") || pathItem.endsWith("/..") : pathItem.endsWith("\\.") || pathItem.endsWith("\\.."));
} 
// If a path is not a directory, and it isn't empty, it must be a file
function isFile(pathItem) {
    if (pathItem === "") {
        return false;
    }
    return !isDirectory(pathItem);
}

节点版本:v11.10.0 - 2019年2月

最后的想法:为什么要访问文件系统?


如果文件夹名称以点结尾,例如.gitmyFolder.txt,该怎么办? - wow ow
你必须理解posix文件路径约定(其中Windows在内核级别上是符合posix的一部分)。请阅读https://dev59.com/9nNA5IYBdhLWcg3wZ81R和https://en.wikipedia.org/wiki/Talk:Path_(computing)#Filepath_Misconceptions。 - TamusJRoyce
我并没有真正回答这个问题,.git和myFolder.txt可以是文件夹或文件,你需要检查才能确定。由于文件夹也被视为文件,因此不能同时拥有同名的文件夹和文件。.git/.和myFolder.txt/.都是文件夹,.git/和myFolder.txt/则是该文件夹中的所有文件。man readline文档对此进行了解释(但比较晦涩)。孤独的.是特殊的,包含.的文件/文件夹则不是。 - TamusJRoyce
"."和".."都是特殊的。 - TamusJRoyce

1
我可以使用以下代码检查目录或文件是否存在:
// This returns if the file is not a directory.
if(fs.lstatSync(dir).isDirectory() == false) return;

// This returns if the folder is not a file.
if(fs.lstatSync(dir).isFile() == false) return;

-1

返回类型的函数

我喜欢咖啡

type: (uri)-> (fina) ->
  fs.lstat uri, (erro,stats) ->
    console.log {erro} if erro
    fina(
      stats.isDirectory() and "directory" or
      stats.isFile() and "document" or
      stats.isSymbolicLink() and "link" or
      stats.isSocket() and "socket" or
      stats.isBlockDevice() and "block" or
      stats.isCharacterDevice() and "character" or
      stats.isFIFO() and "fifo"
    )

用法:

dozo.type("<path>") (type) ->
  console.log "type is #{type}"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接