天天看點

nodejs中的檔案系統簡介nodejs中的檔案系統子產品Promise版本的fs檔案描述符fs.stat檔案狀态資訊fs的檔案讀寫fs的檔案夾操作path操作

簡介

nodejs使用了異步IO來提升服務端的處理效率。而IO中一個非常重要的方面就是檔案IO。今天我們會詳細介紹一下nodejs中的檔案系統和IO操作。

nodejs中的檔案系統子產品

nodejs中有一個非常重要的子產品叫做fs。這個子產品提供了許多非常實用的函數來通路檔案系統并與檔案系統進行互動。

簡單統計一下,fs提供了下面這麼多種使用的檔案操作方法:

  • fs.access(): 檢查檔案是否存在,以及 Node.js 是否有權限通路。
  • fs.appendFile(): 追加資料到檔案。如果檔案不存在,則建立檔案。
  • fs.chmod(): 更改檔案(通過傳入的檔案名指定)的權限。相關方法:fs.lchmod()、fs.fchmod()。
  • fs.chown(): 更改檔案(通過傳入的檔案名指定)的所有者和群組。相關方法:fs.fchown()、fs.lchown()。
  • fs.close(): 關閉檔案描述符。
  • fs.copyFile(): 拷貝檔案。
  • fs.createReadStream(): 建立可讀的檔案流。
  • fs.createWriteStream(): 建立可寫的檔案流。
  • fs.link(): 建立指向檔案的硬連結。
  • fs.mkdir(): 建立檔案夾。
  • fs.mkdtemp(): 建立臨時目錄。
  • fs.open(): 設定檔案模式。
  • fs.readdir(): 讀取目錄的内容。
  • fs.readFile(): 讀取檔案的内容。相關方法:fs.read()。
  • fs.readlink(): 讀取符号連結的值。
  • fs.realpath(): 将相對的檔案路徑指針(.、..)解析為完整的路徑。
  • fs.rename(): 重命名檔案或檔案夾。
  • fs.rmdir(): 删除檔案夾。
  • fs.stat(): 傳回檔案(通過傳入的檔案名指定)的狀态。相關方法:fs.fstat()、fs.lstat()。
  • fs.symlink(): 建立檔案的符号連結。
  • fs.truncate(): 将傳遞的檔案名辨別的檔案截斷為指定的長度。相關方法:fs.ftruncate()。
  • fs.unlink(): 删除檔案或符号連結。
  • fs.unwatchFile(): 停止監視檔案上的更改。
  • fs.utimes(): 更改檔案(通過傳入的檔案名指定)的時間戳。相關方法:fs.futimes()。
  • fs.watchFile(): 開始監視檔案上的更改。相關方法:fs.watch()。
  • fs.writeFile(): 将資料寫入檔案。相關方法:fs.write()。
注意,上面fs提供的方法都是異步的,所謂異步的意思是,這些方法都提供了回調函數,友善異步觸發相應的處理邏輯。

我們舉一個簡單的讀取檔案的例子:

const fs = require('fs')

fs.readFile('/tmp/flydean.txt', 'utf8' , (err, data) => {
  if (err) {
    console.error(err)
    return
  }
  console.log(data)
})           

上面的例子中,我們從/tmp檔案中讀取了一個flydean.txt檔案。并在callback函數中分别對異常和正常的資料進行了處理。

fs在提供異步方法的同時,還提供了同步的方法調用,這個同步的方法就是在異步方法後面加上Sync:

const fs = require('fs')

try {
  const data = fs.readFileSync('/tmp/flydean.txt', 'utf8')
  console.log(data)
} catch (err) {
  console.error(err)
}           

看下将上面的方法改寫成同步方法之後的樣子。

兩者的差別就是,同步方法會阻塞,一直等到file讀取完成。

Promise版本的fs

異步操作怎麼能少得了Promsie, 因為fs中的操作都是異步的,如果大家不想通過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('出錯:', error.message);
  }
})('/tmp/flydean.txt');           

fs的promise版本在fs/promises下面,上面的例子中我們使用了async和await,以同步的方式編寫異步程式,非常的友善。

檔案描述符

檔案描述符就是指在nodejs中,當我們使用fs.open方法獲得的這個傳回值。

我們可以通過這個檔案描述符來進步和檔案進行互動操作。

const fs = require('fs')

fs.open('/tmp/flydean.txt', 'r', (err, fd) => {
  //fd 是檔案描述符。
})           

上面的open方法的第二個參數表示以隻讀的方式打開檔案。

我們看下常用的檔案系統标志:

  • 'r': 打開檔案用于讀取。 如果檔案不存在,則會發生異常。
  • 'r+': 打開檔案用于讀取和寫入。 如果檔案不存在,則會發生異常。
  • 'w': 打開檔案用于寫入。 如果檔案不存在則建立檔案,如果檔案存在則截斷檔案。
  • 'w+': 打開檔案用于讀取和寫入。 如果檔案不存在則建立檔案,如果檔案存在則截斷檔案。
  • 'a': 打開檔案用于追加。 如果檔案不存在,則建立該檔案。
  • 'a+': 打開檔案用于讀取和追加。 如果檔案不存在,則建立該檔案。

當然,上面的例子也可以用openSync來改寫:

const fs = require('fs')

try {
  const fd = fs.openSync('/tmp/flydean.txt', 'r')
} catch (err) {
  console.error(err)
}           

fs.stat檔案狀态資訊

nodejs提供了一個fs.Stats類,用來描述檔案的狀态資訊。

Stats提供了一些非常有用的方法來判斷檔案的狀态:

比如:

stats.isDirectory(),stats.isFile(),stats.isSocket(),stats.isSymbolicLink(),stats.ctime等。

stats還提供了一些關于檔案時間相關的選項:

  • atime "通路時間" - 上次通路檔案資料的時間。
  • mtime "修改時間" - 上次修改檔案資料的時間。
  • ctime "更改時間" - 上次更改檔案狀态(修改索引節點資料)的時間。
  • birthtime "建立時間" - 建立檔案的時間。

我們看一下怎麼擷取到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将會作為fs.stat的回調函數參數傳入。通過fs.Stats,我們再進行一系列的操作。

fs的檔案讀寫

上面我們介紹了使用fs進行檔案讀取操作,下面我們來介紹怎麼使用fs來進行檔案寫入操作:

const fs = require('fs')

const content = 'www.flydean.com'

fs.writeFile('/tmp/flydean.txt', content, err => {
  if (err) {
    console.error(err)
    return
  }
  //檔案寫入成功。
})           

上面是一個callback版本的,我們再看一個同步版本的:

const fs = require('fs')

const content = 'www.flydean.com'

try {
  const data = fs.writeFileSync('/tmp/flydean.txt', content)
  //檔案寫入成功。
} catch (err) {
  console.error(err)
}           

writeFile還支援一個額外的options參數,在options參數中,我們可以指定檔案寫入的flag标記位,比如:r+,w+,a,a+等等。

fs.writeFile('/tmp/flydean.txt', content, { flag: 'a+' }, err => {})           

當然,除了使用a+表示append到檔案末尾之外,fs還提供了一個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相對而言負責點,我們舉例說明:

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兩種方式來讀取目錄中的檔案。

大家可以看下其中的差別。

path操作

最後,我們介紹一個和file特别相關的path操作,它提供了一些實用工具,用于處理檔案和目錄的路徑。

path代表的是路徑。我們通過下面的方式來使用path:

const path = require('path')           

為什麼需要path呢?我們知道這個世界上大約有兩種風格的作業系統,windows和POSIX。

在這兩種作業系統中,路徑的表達方式是不一樣的。是以,我們需要一個通用的path子產品來為我們解決這個差異。

我們可以通過一個例子來觀察這個差異:

在windows上:

path.basename('C:\\temp\\myfile.html');
// 傳回: 'myfile.html'           

在POSIX上:

path.basename('C:\\temp\\myfile.html');
// 傳回: 'C:\\temp\\myfile.html'           

我們先來看一下path.basename這個方法,是用來傳回path 的最後一部分。

上面的例子中,我們向windows傳入了一個windows風格的path,是以可以正常解析,得到正常的結果。

而在POSIX環境中,我們傳入了一個windows風格的路徑,無法正常解析,直接傳回整個的結果。

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' 從目前目錄開始解析,獲得相對路徑的絕對路徑

path.normalize('/tmp/flydean..//test.txt') ///tmp/test.txt  嘗試計算實際的路徑           

本文作者:flydean程式那些事

本文連結:

http://www.flydean.com/nodejs-file-system/

本文來源:flydean的部落格

歡迎關注我的公衆号:「程式那些事」最通俗的解讀,最深刻的幹貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!

繼續閱讀