簡(jiǎn)介
nodejs使用了異步IO來提升服務(wù)端的處理效率。而IO中一個(gè)非常重要的方面就是文件IO。今天我們會(huì)詳細(xì)介紹一下nodejs中的文件系統(tǒng)和IO操作。
nodejs中的文件系統(tǒng)模塊
nodejs中有一個(gè)非常重要的模塊叫做fs。這個(gè)模塊提供了許多非常實(shí)用的函數(shù)來訪問文件系統(tǒng)并與文件系統(tǒng)進(jìn)行交互。
簡(jiǎn)單統(tǒng)計(jì)一下,fs提供了下面這么多種使用的文件操作方法:
- fs.access(): 檢查文件是否存在,以及 Node.js 是否有權(quán)限訪問。
- fs.appendFile(): 追加數(shù)據(jù)到文件。如果文件不存在,則創(chuàng)建文件。
- fs.chmod(): 更改文件(通過傳入的文件名指定)的權(quán)限。相關(guān)方法:fs.lchmod()、fs.fchmod()。
- fs.chown(): 更改文件(通過傳入的文件名指定)的所有者和群組。相關(guān)方法:fs.fchown()、fs.lchown()。
- fs.close(): 關(guān)閉文件描述符。
- fs.copyFile(): 拷貝文件。
- fs.createReadStream(): 創(chuàng)建可讀的文件流。
- fs.createWriteStream(): 創(chuàng)建可寫的文件流。
- fs.link(): 新建指向文件的硬鏈接。
- fs.mkdir(): 新建文件夾。
- fs.mkdtemp(): 創(chuàng)建臨時(shí)目錄。
- fs.open(): 設(shè)置文件模式。
- fs.readdir(): 讀取目錄的內(nèi)容。
- fs.readFile(): 讀取文件的內(nèi)容。相關(guān)方法:fs.read()。
- fs.readlink(): 讀取符號(hào)鏈接的值。
- fs.realpath(): 將相對(duì)的文件路徑指針(.、..)解析為完整的路徑。
- fs.rename(): 重命名文件或文件夾。
- fs.rmdir(): 刪除文件夾。
- fs.stat(): 返回文件(通過傳入的文件名指定)的狀態(tài)。相關(guān)方法:fs.fstat()、fs.lstat()。
- fs.symlink(): 新建文件的符號(hào)鏈接。
- fs.truncate(): 將傳遞的文件名標(biāo)識(shí)的文件截?cái)酁橹付ǖ拈L(zhǎng)度。相關(guān)方法:fs.ftruncate()。
- fs.unlink(): 刪除文件或符號(hào)鏈接。
- fs.unwatchFile(): 停止監(jiān)視文件上的更改。
- fs.utimes(): 更改文件(通過傳入的文件名指定)的時(shí)間戳。相關(guān)方法:fs.futimes()。
- fs.watchFile(): 開始監(jiān)視文件上的更改。相關(guān)方法:fs.watch()。
- fs.writeFile(): 將數(shù)據(jù)寫入文件。相關(guān)方法:fs.write()。
注意,上面fs提供的方法都是異步的,所謂異步的意思是,這些方法都提供了回調(diào)函數(shù),方便異步觸發(fā)相應(yīng)的處理邏輯。
我們舉一個(gè)簡(jiǎn)單的讀取文件的例子:
const fs = require('fs')
fs.readFile('/tmp/flydean.txt', 'utf8' , (err, data) => {
if (err) {
console.error(err)
return
}
console.log(data)
})
上面的例子中,我們從/tmp文件中讀取了一個(gè)flydean.txt文件。并在callback函數(shù)中分別對(duì)異常和正常的數(shù)據(jù)進(jìn)行了處理。
fs在提供異步方法的同時(shí),還提供了同步的方法調(diào)用,這個(gè)同步的方法就是在異步方法后面加上Sync:
const fs = require('fs')
try {
const data = fs.readFileSync('/tmp/flydean.txt', 'utf8')
console.log(data)
} catch (err) {
console.error(err)
}
看下將上面的方法改寫成同步方法之后的樣子。
兩者的區(qū)別就是,同步方法會(huì)阻塞,一直等到file讀取完成。
Promise版本的fs
異步操作怎么能少得了Promsie, 因?yàn)閒s中的操作都是異步的,如果大家不想通過callback來使用fs的話,fs也提供了Promise版本。
還是剛剛的readfile的例子,我們看看如果使用Promise該怎么處理:
const fs = require('fs/promises');
(async function(path) {
try {
await fs.readFile(path, 'utf8' );
console.log(`讀取文件成功 ${path}`);
} catch (error) {
console.error('出錯(cuò):', error.message);
}
})('/tmp/flydean.txt');
fs的promise版本在fs/promises下面,上面的例子中我們使用了async和await,以同步的方式編寫異步程序,非常的方便。
文件描述符
文件描述符就是指在nodejs中,當(dāng)我們使用fs.open方法獲得的這個(gè)返回值。
我們可以通過這個(gè)文件描述符來進(jìn)步和文件進(jìn)行交互操作。
const fs = require('fs')
fs.open('/tmp/flydean.txt', 'r', (err, fd) => {
//fd 是文件描述符。
})
上面的open方法的第二個(gè)參數(shù)表示以只讀的方式打開文件。
我們看下常用的文件系統(tǒng)標(biāo)志:
'r': 打開文件用于讀取。 如果文件不存在,則會(huì)發(fā)生異常。
'r+': 打開文件用于讀取和寫入。 如果文件不存在,則會(huì)發(fā)生異常。
'w': 打開文件用于寫入。 如果文件不存在則創(chuàng)建文件,如果文件存在則截?cái)辔募?/p>
'w+': 打開文件用于讀取和寫入。 如果文件不存在則創(chuàng)建文件,如果文件存在則截?cái)辔募?/p>
'a': 打開文件用于追加。 如果文件不存在,則創(chuàng)建該文件。
'a+': 打開文件用于讀取和追加。 如果文件不存在,則創(chuàng)建該文件。
當(dāng)然,上面的例子也可以用openSync來改寫:
const fs = require('fs')
try {
const fd = fs.openSync('/tmp/flydean.txt', 'r')
} catch (err) {
console.error(err)
}
fs.stat文件狀態(tài)信息
nodejs提供了一個(gè)fs.Stats類,用來描述文件的狀態(tài)信息。
Stats提供了一些非常有用的方法來判斷文件的狀態(tài):
比如:
stats.isDirectory(),stats.isFile(),stats.isSocket(),stats.isSymbolicLink(),stats.ctime等。
stats還提供了一些關(guān)于文件時(shí)間相關(guān)的選項(xiàng):
- atime "訪問時(shí)間" - 上次訪問文件數(shù)據(jù)的時(shí)間。
- mtime "修改時(shí)間" - 上次修改文件數(shù)據(jù)的時(shí)間。
- ctime "更改時(shí)間" - 上次更改文件狀態(tài)(修改索引節(jié)點(diǎn)數(shù)據(jù))的時(shí)間。
- birthtime "創(chuàng)建時(shí)間" - 創(chuàng)建文件的時(shí)間。
我們看一下怎么獲取到fs.stat:
const fs = require('fs')
fs.stat('/tmp/flydean.txt', (err, stats) => {
if (err) {
console.error(err)
return
}
stats.isFile() //true
stats.isDirectory() //false
stats.isSymbolicLink() //false
stats.size //文件大小
})
fs.Stats將會(huì)作為fs.stat的回調(diào)函數(shù)參數(shù)傳入。通過fs.Stats,我們?cè)龠M(jìn)行一系列的操作。
fs的文件讀寫
上面我們介紹了使用fs進(jìn)行文件讀取操作,下面我們來介紹怎么使用fs來進(jìn)行文件寫入操作:
const fs = require('fs')
const content = 'www.flydean.com'
fs.writeFile('/tmp/flydean.txt', content, err => {
if (err) {
console.error(err)
return
}
//文件寫入成功。
})
上面是一個(gè)callback版本的,我們?cè)倏匆粋€(gè)同步版本的:
const fs = require('fs')
const content = 'www.flydean.com'
try {
const data = fs.writeFileSync('/tmp/flydean.txt', content)
//文件寫入成功。
} catch (err) {
console.error(err)
}
writeFile還支持一個(gè)額外的options參數(shù),在options參數(shù)中,我們可以指定文件寫入的flag標(biāo)記位,比如:r+,w+,a,a+等等。
fs.writeFile('/tmp/flydean.txt', content, { flag: 'a+' }, err => {})
當(dāng)然,除了使用a+表示append到文件末尾之外,fs還提供了一個(gè)appendFile方法來向文件末尾輸出:
const fs = require('fs')
const content = 'www.flydean.com'
fs.appendFile('/tmp/flydean.txt', content, err => {
if (err) {
console.error(err)
return
}
//文件append成功。
})
fs的文件夾操作
有文件就有文件夾,fs提供了一系列的文件夾操作,比如:
mkdir,readdir,rename rmdir操作。
readdir相對(duì)而言負(fù)責(zé)點(diǎn),我們舉例說明:
const fs = require('fs')
const folderPath = '/tmp'
fs.readdir(folderPath, function(err,files){
if(err){
console.log(err);
}
files.map(file => console.log(file));
})
fs.readdirSync(folderPath).map(fileName => {
console.log(fileName);
})
上面的例子中,我們分別使用了readdir和readdirSync兩種方式來讀取目錄中的文件。
大家可以看下其中的區(qū)別。
path操作
最后,我們介紹一個(gè)和file特別相關(guān)的path操作,它提供了一些實(shí)用工具,用于處理文件和目錄的路徑。
path代表的是路徑。我們通過下面的方式來使用path:
const path = require('path')
為什么需要path呢?我們知道這個(gè)世界上大約有兩種風(fēng)格的操作系統(tǒng),windows和POSIX。
在這兩種操作系統(tǒng)中,路徑的表達(dá)方式是不一樣的。所以,我們需要一個(gè)通用的path模塊來為我們解決這個(gè)差異。
我們可以通過一個(gè)例子來觀察這個(gè)差異:
在windows上:
path.basename('C:\\temp\\myfile.html');
// 返回: 'myfile.html'
在POSIX上:
path.basename('C:\\temp\\myfile.html');
// 返回: 'C:\\temp\\myfile.html'
我們先來看一下path.basename這個(gè)方法,是用來返回path 的最后一部分。
上面的例子中,我們向windows傳入了一個(gè)windows風(fēng)格的path,所以可以正常解析,得到正常的結(jié)果。
而在POSIX環(huán)境中,我們傳入了一個(gè)windows風(fēng)格的路徑,無法正常解析,直接返回整個(gè)的結(jié)果。
path還有很多非常有用的方法,比如:
const notes = '/tmp/notes.txt'
path.dirname(notes) // /tmp
path.basename(notes) // notes.txt
path.extname(notes) // .txt
path.join('/', 'tmp', 'notes.txt') //'/tmp/notes.txt'
path.resolve('notes.txt') //'/Users/flydean/notes.txt' 從當(dāng)前目錄開始解析,獲得相對(duì)路徑的絕對(duì)路徑
path.normalize('/tmp/flydean..//test.txt') ///tmp/test.txt 嘗試計(jì)算實(shí)際的路徑
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/nodejs-file-system/
本文來源:flydean的博客
歡迎關(guān)注我的公眾號(hào):「程序那些事」最通俗的解讀,最深刻的干貨,最簡(jiǎn)潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!