NodeJS 在节点的不区分大小写的文件系统中获取实际文件名的有效方法

bvhaajcl  于 2022-12-22  发布在  Node.js
关注(0)|答案(1)|浏览(238)

不是this question的复制品。这个问题不是关于Windows的。这是一个跨操作系统的通用问题。
除了获取目录和查找匹配的名称之外,是否有有效的方法可以获取node.js中文件名的正确大小写?
示例:假设我有一个包含3个文件的文件夹

+-someFolder
  +-fooBar.txt
  +-Moo.txt
  +-ReadMe.txt

我需要一个传递somefolder/readme.txt的函数返回someFolder/ReadMe.txt
AFAICT唯一的方法是调用fs.readDirfs.readDirSync,看看是否有匹配的文件,类似于

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

function getActualFilename(filename) {
  if(!fs.existsSync(filename)) {
    throw new Error(`${filename} does not exist`);
  }
  return getActualFilenameImpl(filename);
}

function getActualFilenameImpl(filename) {
  const lcFilename = path.basename(filename).toLowerCase();
  // handles passing in `c:\\`
  if (!lcFilename) {
    return filename.toUpperCase();
  }

  const dirname = path.dirname(filename);
  let filenames;
  try {
    filenames = fs.readdirSync(dirname);
  } catch (e) {
    // we already verified the path exists above so if this
    // happens it means the OS won't let use get a listing (UNC root on windows)
    // so it's the best we can do
    return filename;
  }
  const matches = filenames.filter(name => lcFilename === name.toLowerCase());
  if (!matches.length) {
    throw new Error(`${filename} does not exist`);
  }

  const realname = matches[0];
  if (dirname !== '.') {
    if (dirname.endsWith('/') || dirname.endsWith('\\')) {
      return path.join(dirname, realname);
    } else {
      return path.join(getActualFilenameImpl(dirname), realname);
    }
  } else {
    return realname;
  }
}

上面的代码很蹩脚。尝试不同的东西已经清楚地表明有很多边缘情况。特别是在Windows上,UNC路径失败,因为一旦你到达网络路径根,你就不能调用fs.readdirSync。我不知道该调用哪些函数来确定路径的分隔位置,以及如何获取正确的case路径,因为它可能是一组完全独立的Windows API调用(如调用net use用于显示共享的任何函数)等...
我确实注意到path.dirname在到达UNC路径时停止删除尾部斜杠,所以使用它来尝试计算何时停止尝试。
注:

  • 我知道,例如在Linux上(也可以在Mac上),文件系统可能是大小写敏感的,我必须检查一下,但我主要关心的是Windows和标准macOS,稍后将处理大小写敏感的问题。
  • 我也知道JavaScript的toLowerCase可能不符合操作系统的大小写不敏感概念,所以如果有一个解决方案,考虑到这一点,也将是伟大的!
  • 我知道我可以缓存结果或目录列表以加快速度,但我想知道是否有一些其他函数可以使用,而不读取整个目录列表。

我实际上正在努力解决几个问题,并愿意听取其他建议
问题1:在应用程序特定的数据库中存储什么文件名。似乎最好存储实际的文件名。参见#3
问题2:判断2个文件名是否引用同一个文件/文件夹。因此,如果用户指定SomeFolder/foobar.txtsomefolder/FOOBAR.txt,我不希望它们显示为2个单独的文件,如果它们实际上是同一个文件。我需要我的应用程序知道它们引用同一个文件。我认为我可以为此调用fs.stat并检查ino字段是否匹配?
问题三:与问题1相关,重新加载与文件相关的元数据。如果用户在某个时候指定SomeFolder/foobar.txt,并且我的应用生成了与文件相关的元数据,然后在其他一些时间点,他们指定somefolder/FOOBAR.txt,我需要找到匹配的元数据。我目前的想法是通过查找实际的文件名,并使用它来匹配这个问题将得到解决。虽然我想如果他们把文件从FooBar.txt重命名为foobar.txt,它会丢失元数据。虽然我不确定我是否关心这种情况,因为如果他们把文件从FooBar.txt重命名为SomethingElse.txt,我肯定不关心我是否丢失元数据。
也就是说,也许我应该把ino作为密钥存储在我的数据库中?我不确定我是否能接受这个想法,但这是一种可能性,我很想知道其他人是否会这样做。一些检查显示,至少在macOS上,ino在同一个驱动器上移动和重命名时保持不变,这对我的用例来说是一件好事。另一方面,我'我假设ino只对每个文件系统有效,因此如果我安装了2个不同的驱动器,我可能会得到冲突的ino。

const stat = fs.statSync(filename);
const key = `${stat.dev}:${stat.ino}`;

虽然我不知道stat.dev对于可移动存储是否总是相同的,但我认为它不是。所以看起来文件名作为密钥可能更好?

ygya80vv

ygya80vv1#

只要文件系统在不同情况下不保持同名文件之间的连接(我不知道任何这样的文件系统),除了扫描目录之外就没有其他解决方案,因为在任何级别上都没有为此提供API。
因此,您必须按照建议手动扫描,或者使用glob之类的库来查找文件,同时忽略大小写。
但是你说你也有数据库中的文件名。所以如果你能确保数据库中的文件名与文件系统中的文件名完全匹配,那么你应该能够通过不区分大小写的数据库查询找到不同大小写的文件。如果它是一个SQL数据库,那么它应该已经提供了这个功能。如果它是一个更原始的数据存储,您可以添加另一个始终为小写的filename属性,以便您可以与之匹配以找到真实的的文件。

相关问题