繁体   English   中英

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

[英]Efficient way to get the actual filename in a case-insensitive filesystem in node

不是这个问题的骗局。 这个问题与 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;
  }
}

上面的代码非常 hacky。 尝试不同的事情已经清楚地表明有很多边缘情况。 在 Windows 上,特别是 UNC 路径失败,因为一旦到达网络路径根目录就无法调用fs.readdirSync 我不知道调用什么函数来弄清楚路径在哪里分开,然后如何为可能是一组完全独立的 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字段是否匹配?

问题3:与问题1相关,重新加载与文件相关的元数据。 如果用户在某个时候指定SomeFolder/foobar.txt并且我的应用程序生成了与该文件相关的元数据,那么在其他某个时间点他们指定somefolder/FOOBAR.txt我需要找到匹配的元数据。 我目前的想法是通过查找实际文件名并使用它来匹配这个问题就可以解决。 虽然我想如果他们将文件从FooBar.txt重命名为foobar.txt ,它会丢失元数据。 我不确定我是否关心这种情况,因为如果他们从FooBar.txt重命名为SomethingElse.txt我绝对不关心我是否丢失了元数据。

也就是说,也许我应该将ino作为密钥存储在我的数据库中? 不确定我是否对这个想法感到满意,但这是一种可能性,并且很想知道其他人是否这样做。 一些检查表明,至少在 macOS 上,ino 在同一驱动器上的移动和重命名之间保持不变,这对我的用例来说是一件好事。 另一方面,我假设 ino 仅对每个文件系统有效,因此如果我安装了 2 个不同的驱动器,我可能会发生 inos 冲突。 我可以使用devino作为密钥,如

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

虽然我不知道stat.dev是否总是与可移动存储相同。 我想不是。 所以看起来文件名作为键可能更好?

只要文件系统在不同情况下不保持同名文件之间的连接(而且我不知道任何这样的文件系统),除了扫描目录之外就没有其他解决方案,因为根本没有提供 API为此,在任何级别。

因此,您必须按照已经建议的那样手动扫描,或者使用像glob这样的库在忽略大小写的情况下查找文件。

但是你说你也有数据库中的文件名。 因此,如果您可以确保数据库中的文件名与文件系统中的文件名完全匹配,那么您应该能够通过不区分大小写的数据库查询来找到不同情况下的文件。 如果它是一个 SQL 数据库,那么它应该已经提供了这个功能。 如果它是一个更原始的数据存储,你可以添加另一个文件名属性,它总是小写的,这样你就可以匹配它来找到真正的文件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM