Node.js 指南(使用不一樣的文件系統)

使用不一樣的文件系統

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形式保留就同樣簡單。

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事務的剖析

下一篇:流中的背壓

相關文章
相關標籤/搜索