簡(jiǎn)介
nodejs使用了異步IO來(lái)提升服務(wù)端的處理效率桅咆。而IO中一個(gè)非常重要的方面就是文件IO螟深。今天我們會(huì)詳細(xì)介紹一下nodejs中的文件系統(tǒng)和IO操作赠尾。
nodejs中的文件系統(tǒng)模塊
nodejs中有一個(gè)非常重要的模塊叫做fs毡证。這個(gè)模塊提供了許多非常實(shí)用的函數(shù)來(lái)訪問(wèn)文件系統(tǒng)并與文件系統(tǒng)進(jìn)行交互转砖。
簡(jiǎn)單統(tǒng)計(jì)一下赔癌,fs提供了下面這么多種使用的文件操作方法:
- fs.access(): 檢查文件是否存在诞外,以及 Node.js 是否有權(quán)限訪問(wèn)。
- fs.appendFile(): 追加數(shù)據(jù)到文件灾票。如果文件不存在峡谊,則創(chuàng)建文件。
- fs.chmod(): 更改文件(通過(guò)傳入的文件名指定)的權(quán)限刊苍。相關(guān)方法:fs.lchmod()既们、fs.fchmod()。
- fs.chown(): 更改文件(通過(guò)傳入的文件名指定)的所有者和群組正什。相關(guān)方法:fs.fchown()啥纸、fs.lchown()。
- fs.close(): 關(guān)閉文件描述符婴氮。
- fs.copyFile(): 拷貝文件斯棒。
- fs.createReadStream(): 創(chuàng)建可讀的文件流。
- fs.createWriteStream(): 創(chuàng)建可寫(xiě)的文件流主经。
- 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(): 返回文件(通過(guò)傳入的文件名指定)的狀態(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(): 更改文件(通過(guò)傳入的文件名指定)的時(shí)間戳。相關(guān)方法:fs.futimes()统翩。
- fs.watchFile(): 開(kāi)始監(jiān)視文件上的更改仙蚜。相關(guān)方法:fs.watch()。
- fs.writeFile(): 將數(shù)據(jù)寫(xiě)入文件厂汗。相關(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)
}
看下將上面的方法改寫(xiě)成同步方法之后的樣子铛嘱。
兩者的區(qū)別就是,同步方法會(huì)阻塞袭厂,一直等到file讀取完成墨吓。
Promise版本的fs
異步操作怎么能少得了Promsie, 因?yàn)閒s中的操作都是異步的,如果大家不想通過(guò)callback來(lái)使用fs的話(huà)纹磺,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秘症,以同步的方式編寫(xiě)異步程序,非常的方便式矫。
文件描述符
文件描述符就是指在nodejs中乡摹,當(dāng)我們使用fs.open方法獲得的這個(gè)返回值。
我們可以通過(guò)這個(gè)文件描述符來(lái)進(jìn)步和文件進(jìn)行交互操作采转。
const fs = require('fs')
fs.open('/tmp/flydean.txt', 'r', (err, fd) => {
//fd 是文件描述符聪廉。
})
上面的open方法的第二個(gè)參數(shù)表示以只讀的方式打開(kāi)文件瞬痘。
我們看下常用的文件系統(tǒng)標(biāo)志:
'r': 打開(kāi)文件用于讀取。 如果文件不存在板熊,則會(huì)發(fā)生異常框全。
'r+': 打開(kāi)文件用于讀取和寫(xiě)入。 如果文件不存在干签,則會(huì)發(fā)生異常津辩。
'w': 打開(kāi)文件用于寫(xiě)入。 如果文件不存在則創(chuàng)建文件容劳,如果文件存在則截?cái)辔募?/p>
'w+': 打開(kāi)文件用于讀取和寫(xiě)入喘沿。 如果文件不存在則創(chuàng)建文件,如果文件存在則截?cái)辔募?/p>
'a': 打開(kāi)文件用于追加鸭蛙。 如果文件不存在摹恨,則創(chuàng)建該文件。
'a+': 打開(kāi)文件用于讀取和追加娶视。 如果文件不存在晒哄,則創(chuàng)建該文件。
當(dāng)然肪获,上面的例子也可以用openSync來(lái)改寫(xiě):
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類(lèi)寝凌,用來(lái)描述文件的狀態(tài)信息。
Stats提供了一些非常有用的方法來(lái)判斷文件的狀態(tài):
比如:
stats.isDirectory()孝赫,stats.isFile()较木,stats.isSocket(),stats.isSymbolicLink()青柄,stats.ctime等伐债。
stats還提供了一些關(guān)于文件時(shí)間相關(guān)的選項(xiàng):
- atime "訪問(wèn)時(shí)間" - 上次訪問(wèn)文件數(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ù)傳入双戳。通過(guò)fs.Stats虹蒋,我們?cè)龠M(jìn)行一系列的操作。
fs的文件讀寫(xiě)
上面我們介紹了使用fs進(jìn)行文件讀取操作飒货,下面我們來(lái)介紹怎么使用fs來(lái)進(jìn)行文件寫(xiě)入操作:
const fs = require('fs')
const content = 'www.flydean.com'
fs.writeFile('/tmp/flydean.txt', content, err => {
if (err) {
console.error(err)
return
}
//文件寫(xiě)入成功魄衅。
})
上面是一個(gè)callback版本的,我們?cè)倏匆粋€(gè)同步版本的:
const fs = require('fs')
const content = 'www.flydean.com'
try {
const data = fs.writeFileSync('/tmp/flydean.txt', content)
//文件寫(xiě)入成功塘辅。
} catch (err) {
console.error(err)
}
writeFile還支持一個(gè)額外的options參數(shù)晃虫,在options參數(shù)中,我們可以指定文件寫(xiě)入的flag標(biāo)記位莫辨,比如:r+傲茄,w+毅访,a,a+等等盘榨。
fs.writeFile('/tmp/flydean.txt', content, { flag: 'a+' }, err => {})
當(dāng)然喻粹,除了使用a+表示append到文件末尾之外,fs還提供了一個(gè)appendFile方法來(lái)向文件末尾輸出:
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)郁竟,我們舉例說(shuō)明:
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兩種方式來(lái)讀取目錄中的文件。
大家可以看下其中的區(qū)別棚亩。
path操作
最后蓖议,我們介紹一個(gè)和file特別相關(guān)的path操作,它提供了一些實(shí)用工具讥蟆,用于處理文件和目錄的路徑勒虾。
path代表的是路徑。我們通過(guò)下面的方式來(lái)使用path:
const path = require('path')
為什么需要path呢瘸彤?我們知道這個(gè)世界上大約有兩種風(fēng)格的操作系統(tǒng)修然,windows和POSIX。
在這兩種操作系統(tǒng)中质况,路徑的表達(dá)方式是不一樣的愕宋。所以,我們需要一個(gè)通用的path模塊來(lái)為我們解決這個(gè)差異结榄。
我們可以通過(guò)一個(gè)例子來(lái)觀察這個(gè)差異:
在windows上:
path.basename('C:\\temp\\myfile.html');
// 返回: 'myfile.html'
在POSIX上:
path.basename('C:\\temp\\myfile.html');
// 返回: 'C:\\temp\\myfile.html'
我們先來(lái)看一下path.basename這個(gè)方法掏婶,是用來(lái)返回path 的最后一部分。
上面的例子中潭陪,我們向windows傳入了一個(gè)windows風(fēng)格的path,所以可以正常解析最蕾,得到正常的結(jié)果依溯。
而在POSIX環(huán)境中,我們傳入了一個(gè)windows風(fēng)格的路徑瘟则,無(wú)法正常解析黎炉,直接返回整個(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)前目錄開(kāi)始解析慷嗜,獲得相對(duì)路徑的絕對(duì)路徑
path.normalize('/tmp/flydean..//test.txt') ///tmp/test.txt 嘗試計(jì)算實(shí)際的路徑
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/nodejs-file-system/
本文來(lái)源:flydean的博客
歡迎關(guān)注我的公眾號(hào):「程序那些事」最通俗的解讀淀弹,最深刻的干貨,最簡(jiǎn)潔的教程庆械,眾多你不知道的小技巧等你來(lái)發(fā)現(xiàn)薇溃!