资讯专栏INFORMATION COLUMN

Node.js 指南(使用不同的文件系统)

RebeccaZhong / 1341人阅读

摘要:避免使用最低公分母方法你可能想让你的程序像最低公分母文件系统一样,通过将所有文件名规范化为大写,将所有文件名规范化为格式,并将所有文件时间戳标准化为秒分辨率,这是最小公分母的方法。

使用不同的文件系统

Node公开了文件系统的许多功能,但并非所有文件系统都相似,以下是建议的最佳实践,以便在使用不同的文件系统时保持代码简单和安全。

文件系统行为

在使用文件系统之前,你需要知道它的行为方式,不同的文件系统表现不同,并且具有比其他或多或少的功能:区分大小写、不区分大小写、大小写保留、Unicode形式保留、时间戳解析、扩展属性、inode、Unix权限、备用数据流等。

警惕从process.platform推断文件系统行为,例如,不要假设因为你的程序在Darwin上运行,因此你正在处理不区分大小写的文件系统(HFS+),因为用户可能正在使用区分大小写的文件系统(HFSX)。类似地,不要假设因为你的程序在Linux上运行,因此你正在处理支持Unix权限和inode的文件系统,因为你可能位于特定的外部驱动器、USB或网络驱动器上。

操作系统可能不容易推断文件系统行为,但并不会丢失所有内容,你可以探测文件系统以查看它的实际行为,而不是保留每个已知文件系统和行为的列表(总是不完整),某些易于探测的特征的存在或缺失,往往足以推断其他更难探测的特征的行为。

请记住,某些用户可能在工作树中的各种路径上安装了不同的文件系统。

避免使用最低公分母方法

你可能想让你的程序像最低公分母文件系统一样,通过将所有文件名规范化为大写,将所有文件名规范化为NFC Unicode格式,并将所有文件时间戳标准化为1秒分辨率,这是最小公分母的方法。

不要这样做,你只能安全地与文件系统进行交互,该文件系统在各个方面具有完全相同的最小公分母特征,你将无法以用户期望的方式使用更高级的文件系统,并且你将遇到文件名或时间戳冲突,你肯定会通过一系列复杂的相关事件来丢失和损坏用户数据,并且你将创建即使不是不可能解决也很困难的bug。

当你以后需要支持仅具有2秒或24小时时间戳分辨率的文件系统时会发生什么?当Unicode标准进展到包括稍微不同的规范化算法时(如过去发生的那样)会发生什么?

最小公分母方法倾向于尝试仅使用“可移植”系统调用来创建可移植程序,这会导致程序出现漏洞,而且实际上是不可移植的。

采用超集方法

通过采用超集方法充分利用你支持的每个平台,例如,一个可移植备份程序应该在Windows系统之间正确地同步btimes(文件或文件夹的创建时间),并且不应该销毁或更改btimes,即使Linux系统不支持btimes。相同的可移植备份程序应该在Linux系统之间正确同步Unix权限,并且不应该销毁或更改Unix权限,即使在Windows系统上不支持Unix权限。

通过使程序像更高级的文件系统一样处理不同的文件系统,支持所有可能功能的超集:大小写敏感、大小写保留、Unicode形式敏感、Unicode形式保留、Unix权限、高分辨率纳秒时间戳、扩展属性等。

在程序中保留大小写后,如果需要与不区分大小写的文件系统进行交互,则可以始终实现大小写不敏感。但是,如果你放弃了程序中的大小写保留,你就无法安全地与保留大小写的文件系统进行交互,对于Unicode形式保留和时间戳分辨率保留也是如此。

如果文件系统为你提供小写和大写混合的文件名,则将文件名保留在给定的确切大小写中,如果文件系统为你提供混合Unicode格式或NFC或NFD(或NFKC或NFKD)的文件名,则将文件名保留在给定的确切字节序列中,如果文件系统为你提供毫秒时间戳,则保持时间戳以毫秒为单位。

当你使用较小的文件系统时,你可以始终适当地进行下采样,使用运行程序的文件系统的行为所需的比较函数。如果你知道文件系统不支持Unix权限,那么你不应该期望读取你编写的相同Unix权限。如果你知道文件系统不保留大小写,那么你应该准备在程序创建abc时在目录列表中看到ABC。但是,如果你知道文件系统确实保留了大小写,那么在检测文件重命名或文件系统区分大小写时,你应该将ABC视为与abc不同的文件名。

大小写保留

你可以创建一个名为test/abc的目录,有时会惊奇地发现fs.readdir("test")返回["ABC"],这不是Node中的bug,Node返回文件系统存储它的文件名,并非所有文件系统都支持大小写保留,某些文件系统将所有文件名转换为大写(或小写)。

Unicode形式保留

大小写保留和Unicode形式保留是类似的概念,要理解为什么应该保留Unicode形式,请确保首先理解为什么要保留大小写,如果正确理解,Unicode形式保留就一样简单。

Unicode可以使用几个不同的字节序列对相同的字符进行编码,几个字符串可能看起来相同,但具有不同的字节序列。使用UTF-8字符串时,请注意你的期望与Unicode的工作方式一致。正如你不希望所有UTF-8字符编码为单个字节一样,你不应期望几个在人眼看起来相同的UTF-8字符串具有相同的字节表示,这可能是你可以拥有ASCII而不是UTF-8的期望。

你可以创建一个名为test/café的目录(NFC Unicode形式,字节序列<63 61 66 c3 a9>并且string.length === 5)并且有时你会惊讶地发现fs.readdir("test")返回["café"](NFD Unicode形式,字节序列<63 61 66 65 cc 81>并且string.length === 6),这不是Node中的bug。Node返回文件系统存储时的文件名,并非所有文件系统都支持Unicode形式保留。

例如,HFS+会将所有文件名规范化为几乎总是与NFD形式相同的形式,不要指望HFS+的行为与NTFS或EXT4相同,反之亦然。不要试图通过规范化永久地更改数据作为掩盖文件系统之间Unicode差异的漏洞抽象,这会产生问题而不解决任何问题,相反,保留Unicode形似并仅使用规范化作为比较函数。

Unicode形式不敏感

Unicode形式不敏感和Unicode形式保留是两种不同的文件系统行为,经常互相误解。正如在存储和传输文件名时将文件名永久规范化为大写一样,有时不正确地实现了大小写不敏感,因此,在存储和传输文件名时,通过将文件名永久规范化为某种Unicode格式(在HFS+的情况下为NFD),有时会错误地实现Unicode格式不敏感性。通过使用Unicode规范化进行比较,可以并且更好地实现Unicode形式不敏感而不牺牲Unicode形式保留。

比较不同的Unicode形式

Node提供string.normalize("NFC" / "NFD"),你可以使用它将UTF-8字符串规范化为NFC或NFD,你永远不应该存储此函数的输出,而只是将其用作比较函数的一部分,以测试两个UTF-8字符串对于用户是否看起来相同。

你可以使用string1.normalize("NFC") === string2.normalize("NFC")string1.normalize("NFD") === string2.normalize("NFD")作为比较函数,你使用哪种形式并不重要。

规范化很快但你可能希望使用缓存作为比较函数的输入,以避免多次规范化相同的字符串,如果该字符串不在缓存中,则对其进行规范化并对其进行缓存,注意不要存储或保留缓存,只能将其用作缓存。

请注意,使用normalize()要求你的Node版本包含ICU(否则normalize()将返回原始字符串),如果你从网站下载最新版本的Node,那么它将包括ICU。

时间戳分辨率

你可以将文件的mtime(修改时间)设置为1444291759414(毫秒分辨率),并有时惊讶地发现fs.stat将新mtime返回为1444291759000(1秒分辨率)或1444291758000(2秒分辨率),这不是Node中的bug。Node返回文件系统存储它的时间戳,并非所有文件系统都支持纳秒、毫秒或1秒时间戳分辨率。有些文件系统甚至对atime时间戳的分辨率非常粗糙,例如,对于一些FAT文件系统,分辨率为24小时。

不要通过规范化来破坏文件名和时间戳

文件名和时间戳是用户数据,正如你永远不会自动重写用户文件数据以使数据大写或将CRLF规范化为LF行结束一样,因此你不应该通过大小写/Unicode格式/时间戳规范化来更改、干扰或损坏文件名或时间戳,规范化只应用于比较,绝不能用于改变数据。

规范化实际上是有损哈希码,你可以使用它来测试某些类型的等价性(例如,即使它们具有不同的字节序列,几个字符串看起来相同)但你永远不能将它用作实际数据的替代品,你的程序应按原样传递文件名和时间戳数据。

你的程序可以在NFC中创建新数据(或者以其喜欢的任何Unicode形式组合)或使用小写或大写文件名,或者使用2秒的分辨率时间戳,但是你的程序不应该通过强加大小写/Unicode形式/时间戳规范化来破坏现有的用户数据。相反,采用超集方法并在程序中保留大小写、Unicode格式和时间戳分辨率,这样,你就可以安全地与执行相同操作的文件系统进行交互。

适当地使用标准化比较功能

确保正确使用大小写/Unicode形式/时间戳比较功能,如果你正在处理区分大小写的文件系统,请不要使用不区分大小写的文件名比较函数。如果你正在使用Unicode形式敏感文件系统(例如NTFS和大多数保留NFC和NFD或混合Unicode形式的Linux文件系统),请不要使用Unicode形式不敏感的比较函数。如果你正在使用纳秒时间戳分辨率文件系统,请不要以2秒分辨率比较时间戳。

为比较功能的微小差异做好准备

请注意你的比较函数与文件系统的比较函数匹配(或者如果可能的话探测文件系统以查看它实际比较的方式),例如,不区分大小写比简单的toLowerCase()比较复杂,事实上,toUpperCase()通常比toLowerCase()更好(因为它以不同的方式处理某些外语字符)。但更好的方法是探测文件系统,因为每个文件系统都有自己的大小写比较表。

例如,Apple的HFS+将文件名规范化为NFD格式,但这种NFD格式实际上是当前NFD格式的旧版本,有时可能与最新的Unicode标准的NFD格式略有不同,不要指望HFS+ NFD始终与Unicode NFD完全相同。

上一篇:HTTP事务的剖析 下一篇:流中的背压

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/100319.html

相关文章

  • Node.js 指南(ABI稳定性)

    摘要:确保兼容性的剩余责任在于团队维护头文件,这些头文件提供了在编译时在中保持稳定的。中的稳定性提供由几个独立团队维护的头文件,例如,团队维护诸如和之类的头文件。由团队维护,尽管与团队密切合作,但该团队是独立的,并且有自己的时间表和优先级。 ABI稳定性 介绍 应用程序二进制接口(ABI)是程序调用函数和使用其他编译程序的数据结构的一种方法,它是应用程序编程接口(API)的编译版本,换句话说...

    Simon_Zhou 评论0 收藏0
  • Node.js 指南(流中背压)

    摘要:在数据缓冲区已超过或写入队列当前正忙的任何情况下,将返回。当返回值时,背压系统启动,它会暂停传入的流发送任何数据,并等待消费者再次准备就绪,清空数据缓冲区后,将发出事件并恢复传入的数据流。 流中的背压 在数据处理过程中会出现一个叫做背压的常见问题,它描述了数据传输过程中缓冲区后面数据的累积,当传输的接收端具有复杂的操作时,或者由于某种原因速度较慢时,来自传入源的数据就有累积的趋势,就像...

    Tony 评论0 收藏0
  • Node.js 日志输出指南

    摘要:将如下代码写入到文件中,并在环境里执行如图虽然这两个输出看起来可能一样,但系统实际上对它的处理方式有不同。如图如果你没有启动调试日志,则不会看到任何这样的日志输出。 原文地址: https://www.twilio.com/blog/g...原文作者: DOMINIK KUNDEL 翻译作者: icepy 翻译出处: https://github.com/lightningm... ...

    csRyan 评论0 收藏0
  • 【译】Node.js 前端开发指南

    摘要:定时器在和浏览器中的表现形式是相同的。关于定时器的一个重要的事情是,我们提供的延迟不代表在这个时间之后回调就会被执行。它的真正含义是,一旦主线程完成所有操作包括微任务并且没有其它具有更高优先级的定时器,将在此时间之后执行回调。 众成翻译 原文链接 关于作者 2018年6月21日出版​ 本指南面向了解Javascript但尚未十分熟悉Node.js的前端开发人员。我这里不专注于语言本身...

    CntChen 评论0 收藏0
  • Electron指南 - 快速入门

    摘要:快速入门提供了丰富的本地操作系统的,使你能够使用纯来创建桌面应用程序。这并不意味着是一个绑定图形用户界面的库。每个页面在里是运行在自己的进程里,这些进程被称为渲染进程。有些只能在该事件发生后才能被使用。 快速入门 Electron提供了丰富的本地(操作系统)的API,使你能够使用纯JavaScript来创建桌面应用程序。与其它各种的Node.js运行时不同的是Electron专注于桌面...

    SQC 评论0 收藏0

发表评论

0条评论

RebeccaZhong

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<