Node公开了文件系统的许多功能,但并不是全部文件系统都类似,如下是建议的最佳实践,以便在使用不一样的文件系统时保持代码简单和安全。node
在使用文件系统以前,你须要知道它的行为方式,不一样的文件系统表现不一样,而且具备比其余或多或少的功能:区分大小写、不区分大小写、大小写保留、Unicode形式保留、时间戳解析、扩展属性、inode、Unix权限、备用数据流等。算法
警戒从process.platform
推断文件系统行为,例如,不要假设由于你的程序在Darwin上运行,所以你正在处理不区分大小写的文件系统(HFS+),由于用户可能正在使用区分大小写的文件系统(HFSX)。相似地,不要假设由于你的程序在Linux上运行,所以你正在处理支持Unix权限和inode的文件系统,由于你可能位于特定的外部驱动器、USB或网络驱动器上。segmentfault
操做系统可能不容易推断文件系统行为,但并不会丢失全部内容,你能够探测文件系统以查看它的实际行为,而不是保留每一个已知文件系统和行为的列表(老是不完整),某些易于探测的特征的存在或缺失,每每足以推断其余更难探测的特征的行为。缓存
请记住,某些用户可能在工做树中的各类路径上安装了不一样的文件系统。安全
你可能想让你的程序像最低公分母文件系统同样,经过将全部文件名规范化为大写,将全部文件名规范化为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可使用几个不一样的字节序列对相同的字符进行编码,几个字符串可能看起来相同,但具备不一样的字节序列。使用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格式(在HFS+的状况下为NFD),有时会错误地实现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彻底相同。