在NodeJS中操作檔案常見的API

龍恩0707發表於2018-11-11

閱讀目錄

一:如何讀整個檔案內容?

注意:讀取檔案和寫入檔案可能理解很枯燥,但是我每次學習這樣的文章的時候都是為了下一篇文章做好準備的,因為我下一篇文章的demo需要使用到該方面的知識,所以我就會把需要的理解的知識統統記錄一遍的。

在Node.js中,使用fs模組來實現所有檔案及目錄的建立,寫入及刪除操作。

1. 讀檔案的方法:
在執行讀檔案操作時,可以使用readFile方法與readFileSync方法讀取檔案,有Sync等字樣的是同步方法,沒有這個字樣的是非同步方法讀取,同步方法和非同步方法的區別是:同步方法立即返回操作的結果,先看下相關的API:

1)readFileSync同步讀取檔案API
使用方法讀取如下:

fs.readFileSync(filename, [options]);

filename引數:用於指定讀取檔案的完整檔案路徑及檔名。
options引數:該值為一個物件,它可以指定讀取檔案時使用flag屬性指定該檔案採取什麼操作,預設為 'r'; 可以指定值有如下:
'r': 讀取檔案,如果檔案不存在會丟擲異常。
'r+': 讀取並寫入檔案,如果檔案不存在會丟擲異常。
'rs': 同步方式讀取檔案並且通知作業系統忽略本地檔案系統的快取,如果檔案不存在會丟擲異常。
'w': 寫入檔案,如果檔案不存在則建立該檔案,如果檔案已存在則清空檔案內容。
'w+': 讀取並寫入檔案,其他功能和 'w'一樣。
'a': 追加寫入檔案,如果檔案不存在則建立該檔案。
'a+': 讀取並追加寫入檔案,檔案不存在就建立檔案。

在options引數中,我們可以使用 encoding屬性指定使用何種編碼格式來讀取該檔案,屬性值有 'utf-8', 'ascii', 'base64', 下面先看看簡單的demo。如下所示:

先看下目錄結構如下:

### 目錄結構如下:
demo1                                       # 工程名
|   |--- readFile                           # 讀取檔案的資料夾            
|   | |-- index.html                        # html檔案
|   | |-- index.js                          # js檔案
|   | |-- package.json          
|   | |-- node_modules

如下程式碼讀取檔案程式碼:

const fs = require('fs');
try {
  const data = fs.readFileSync('./index.html', 'utf-8');
  // 等待操作結果返回,然後列印結果
  console.log(data);
} catch(e) {
  console.log('讀取檔案發生錯誤');
}

在命令列中執行 node index.js 然後列印結果如下:

2) 非同步讀取檔案的API使用方法如下:
fs.readFile(filename, [options], callback);

filename 和 options 和 同步方法的引數是一樣的。
callback引數 是用於檔案讀取完後執行的回撥函式。如下程式碼所示:

const fs = require('fs');
fs.readFile('./index.html', 'utf-8', (err, data) => {
  if (err) {
    console.log('讀取檔案時發生錯誤');
  } else {
    console.log(data);
  }
});

執行的結果和上面是一樣的。這裡就不截圖了。

二:如何寫入整個檔案內容?

寫入檔案,我們使用fs模組中的 writeFile 方法 或 wirteFileSync方法。writeFile使用方法如下(wirteFileSync方法類似,無非沒有callback引數):

fs.writeFile(filename, data, [options], callback);

filename引數 為指定需要被寫入檔案的完整路徑及檔名。

data引數 為指定需要寫入的內容,值可以是一個字串或一個Buffer物件,該字串或緩衝區中的內容將被完整地寫入到檔案中。

options值為一個物件(可選),選項名為flag,它有如下選項值:

'flag': 用於指定該檔案採取何種操作,預設值為 'w', 含義是寫入檔案。該指定項所有值和上面readFile的值是一樣的。
(檔案不存在就建立該檔案,檔案存在時重寫該檔案)。

'mode': 該屬性用於指定當檔案被開啟時對該檔案的讀寫許可權。預設值為 0666(可讀寫)。還有很多值自己可以百度瞭解下。

'encoding': 用於指定使用何種編碼格式來寫入該檔案。可指定屬性值為 'utf8'、'ascii'、'base64'。

callback: 該引數用於檔案讀取完畢時執行的回撥函式。

下面我們簡單的做一個demo,在我們目錄下的 index.js 檔案中編寫程式碼,在該目錄下建立一個 message.txt檔案並寫入兩行文字。如下程式碼:

const fs = require('fs');
const str = '這是第一行。\r\n這是第二行';
fs.writeFile('./message.txt', str, (err) => {
  if (err) {
    console.log('寫入檔案操作失敗');
  } else {
    console.log('寫入檔案操作成功');
  }
});

如下圖執行命令:

在目錄下會生成 message.txt檔案,如下圖

然後我們開啟檔案,會看到其寫入的內容。

2)寫入buffer物件,如下程式碼:

const fs = require('fs');
const str = new Buffer('我喜愛程式設計');
fs.writeFile('./message.txt', str, (err) => {
  if (err) {
    console.log('寫入檔案操作失敗');
  } else {
    console.log('寫入檔案操作成功');
  }
});

在message.txt中可以看到 '我喜愛程式設計' 幾個字。

3)設定options引數flag屬性選項,比如追加資料這些,如下程式碼:

const fs = require('fs');
const options = {
  flag: 'a'
};
fs.writeFile('./message.txt', '這是追加的資料', options, (err) => {
  if (err) {
    console.log('寫入檔案操作失敗');
  } else {
    console.log('寫入檔案操作成功');
  }
});

message.txt內容程式設計如下了:我喜愛程式設計這是追加的資料

4)複製圖片檔案
我們可以通過readFile方法讀取應用程式對應目錄下的一個圖片檔案,在讀取檔案時使用base64編碼,在該回撥函式中使用writeFile方法,在該方法中使用base64編碼將讀取到的圖片檔案資料複製到另一個圖片檔案中。如下程式碼:

const fs = require('fs');

fs.readFile('./images/1.png', 'base64', (err, data) => {
  fs.writeFile('./images/xx.png', data.toString(), 'base64', (err) => {
    if (err) {
      console.log('寫檔案操作失敗');
    } else {
      console.log('寫檔案操作成功');
    }
  });
});

在image下有一個 1.png, 它會先複製,然後寫入 xx.png中(該檔案之前不存在的,會建立該圖片檔案)。

三:如何在檔案中的指定位置處讀入內容?

3.1)開啟檔案open方法或openSync方法

要實現在檔案中的指定位置處讀入內容,首先我們需要開啟該檔案,fs模組中的open方法或openSync方法開啟檔案。open方法使用方式如下所示:

fs.open(filename, flags, [mode], callback);

前三個引數和readFile的引數是一個意思的。最後一個引數是執行完成後的回撥函式。該回撥函式有兩個引數,第一個是開啟檔案失敗時的錯誤物件,第二個引數為一個整數值,代表開啟檔案時返回的檔案描述符。

下面我們來開啟我們剛剛建立的 message.txt檔案,如下程式碼:

const fs = require('fs');

fs.open('./message.txt', 'r', (err, fd) => {
  console.log(fd);
});

執行結果如下所示:

3.2)從指定的位置讀取檔案

如上面開啟檔案之後,我們可以在其回撥函式使用fs模組中的read方法或readSync方法從檔案的指定位置處讀取檔案,也可以使用fs模組中的write方法或writeSync方法從檔案中指定處寫入資料。

fs.read(fd, buffer, offset, length, position, callback);

fd引數是 上面open開啟檔案返回的檔案描述符。
buffer引數 是一個buffer物件,用於指定將檔案資料讀取到那個快取區中。
offset:用於指定向緩衝區中寫入資料的開始位置。
length:用於指定從檔案中讀取的位元組數。
position: 用於指定讀取檔案時的開始位置。
callback:用於指定檔案操作完畢時的回撥函式。如下所示:

function(err, bytesRead, buffer) {}

err: 為操作失敗時的錯誤物件。
bytesRead: 代表實際讀取的位元組數。
buffer:為被讀取的緩衝區物件。

下面我們來看個demo程式碼如下:

const fs = require('fs');
// message.txt 內容為:我喜愛程式設計這是追加的資料
fs.open('./message.txt', 'r', (err, fd) => {
  const buf = new Buffer(255);
  // 一個漢字的utf編碼為三個位元組資料
  fs.read(fd, buf, 0, 9, 3, (err, bytesRead, buffer) => {
    console.log(buffer.slice(0, bytesRead).toString()); // 喜愛編
  });
});

如上程式碼message.txt內容為:我喜愛程式設計這是追加的資料,position為3,從第三個位元組開始讀取檔案,然後長度為9個位元組,一個漢字3個位元組,因此結果為 '喜愛編';

四:如何在檔案中的指定位置處寫入內容?

在檔案被開啟後,可以使用fs模組中的write方法或writeSync方法從一個快取區中讀取資料並從檔案的指定位置處寫入這些資料。使用方法如下所示:

fs.write(fd, buffer, offset, length, position, callback);

fd引數為描述符。
buffer引數為一個Buffer物件,用於指定從哪個快取區中讀取資料。
offset引數用於指定從快取區中讀取資料時的開始讀取位置。
length引數用於指定從快取區中讀取的位元組數。
position引數值用於指定寫入檔案時的開始位置。
callback引數用於指定檔案寫入操作執行完畢時的回撥函式。該回撥函式如下:

function(err, written, buffer) {}

err引數為寫入失敗時的錯誤物件。
written: 代表被寫入的位元組數。
buffer: 代表被讀取的緩衝區物件。

下面是一個簡單的demo,如下所示:

const fs = require('fs');

const buf = new Buffer('我喜愛程式設計');

fs.open('./message.txt', 'w', (err, fd) => {
  fs.write(fd, buf, 3, 9, 0, (err, written, buffer) => {
    if (err) {
      console.log('寫檔案操作失敗');
    } else {
      console.log('寫檔案操作成功');
    }
  });
}); 

在message.txt中顯示為 喜愛編 這三個字。

五:如何建立與讀取目錄?

5.1) 建立目錄mkdir和mkdirSync方法
在fs模組中,可以使用mkdir方法建立目錄,該方法使用方式如下所示:

fs.mkdir(path, [mode], callback);

path引數用於指定需要被建立的目錄完整路徑及目錄名。
mode引數用於指定該目錄的許可權,預設值為0777(表示任何人可讀寫該目錄)。
callback是回撥函式。
下面是一個簡單的demo,如下所示:

const fs = require('fs');

fs.mkdir('./test', (err) => {
  if (err) {
    console.log('建立目錄操作失敗');
  } else {
    console.log('建立目錄操作成功');
  }
});

如下所示:

5.2)讀取目錄readdir和readdirSync方法
在fs模組中,使用readdir方法讀取目錄,該方法使用如下所示:
fs.readdir(path, callback);

path引數用於指定需要被讀取的目錄的完整路徑及目錄名。
callback引數用於指定讀取目錄操作的回撥函式。該回撥函式如下所示:

function(err, file) {}

err 為讀取目錄操作失敗的回撥函式。
file引數值為一個陣列,讀取到的檔案中的所有檔名。

如下demo所示:

const fs = require('fs');

fs.readdir('./', (err, files) => {
  if (err) {
    console.log('讀取目錄操作失敗');
  } else {
    console.log(files);
  }
});

如下圖所示:

六:如何檢視與修改檔案或目錄的資訊?

6.1)檢視檔案或目錄的資訊

在fs模組中,可以使用stat方法或lstat方法檢視一個檔案或目錄的資訊。這兩個方法唯一的區別是當檢視符號連結檔案的資訊
時,必須使用lstat方法。使用方法如下所示:

fs.stat(path, callback);
fs.lstat(path, callback);

path引數用於被檢視的檔案或目錄的完整路徑及檔名或目錄名。
callback是回撥函式。如下回撥函式
function(err, stats) {}
err 引數值為檢視檔案或目錄資訊操作失敗時觸發的錯誤物件。
stats引數值為一個 fs.Stats物件,該物件有如下一些方法,在這些方法中不包含任何引數。

isFile: 用於判斷被檢視的物件是否為一個檔案,如果是則返回true,否則的話返回false。
isDirectory: 用於判斷被檢視的物件是否為一個目錄,如果是的話返回true,否則的話返回false。

還有很多很多其他的方法,這裡不一一介紹,用的比較少,有需要的話,可以自己百度一下看看。

下面是使用 stat的使用demo,檢視應用程式根目錄下的 message.txt檔案並且在回撥函式中的第二個引數值 fs.Stats物件在控制檯中的輸出有哪些?

const fs = require('fs');

fs.stat('./message.txt', (err, stats) => {
  console.log(stats);
});

如下圖所示顯示:

6.2)檢查檔案或目錄是否存在exists和existsSync方法

在fs模組中,可以使用exists方法檢查一個檔案或目錄是否存在,該方法使用方式如下所示:

fs.exists(path, callback);

path引數:用於指定需要被檢查的檔案或目錄的完整路徑。
callback: 是回撥函式,該回撥函式如下所示:

function(exists) {}

exists引數,當檔案或目錄存在時,該引數值為true,否則的話,該引數值為false。

下面是一個簡單的demo,如下程式碼所示:

const fs = require('fs');

// 該目錄是存在的
fs.exists('./message.txt', (exists) => {
  console.log(exists); // true
});

// 該目錄是不存在的
fs.exists('./message2.txt', (exists) => {
  console.log(exists); // false
});

6.3) 獲取檔案或目錄的絕對路徑realpath和realpathSync方法

在fs模組中,可以使用 realpath方法獲取一個檔案或目錄的絕對路徑,該方法使用如下所示:

fs.realpath(path, [cache], callback);

cache 引數是可選的,path和callback是必須的。
path引數為需要檢視的檔案或目錄的完整路徑。
cache引數為一個物件,其中存放了一些預先指定的路徑。具體的百度下,這個引數用的不多。
callback是回撥函式,該回撥函式有2個引數,err引數值為獲取目錄的絕對路徑失敗的錯誤物件。resolvedPath引數值為獲取
到的檔案或目錄的絕對路徑。

下面是一個簡單的demo,如下所示:

const fs = require('fs');

fs.realpath('./message.txt', (err, resolvedPath) => {
  if (err) {
    throw err;
  } else {
    console.log(resolvedPath);
  }
});

執行如下所示:

6.4) 使用ReadStream物件讀取檔案

在fs模組中,可以使用createReadStream方法建立一個將檔案內容讀取為流資料的ReadStream物件,該方法使用如下所示:

fs.createReadStream(path, [options]);

path 該引數用於指定需要被讀取的檔案的完整路徑及檔名。
options為一個物件,它有如下屬性:

flags: 用於指定對該檔案採取什麼操作,預設值為 'r', 它的用法和readFile方法中的flags屬性一樣的。
encoding: 指定使用什麼編碼來讀取該檔案,可指定的值有 'utf8', 'ascii', 'base64'. 預設值為null.
start: 指定檔案的開始讀取位置。
end: 指定檔案的結束讀取位置。

還有很多其他的引數,這裡不一一講解,可以自行百度下即可。

下面簡單的使用一個demo來了解下使用:如下程式碼:

const fs = require('fs');
/*
 一個漢字三個位元組,message.txt的內容為:我喜愛編寫程式碼
 因此從第三個位置開始,到第十二個位置結束,因此資料應該為 喜愛編寫
*/
const options = {
  start: 3, 
  end: 12
};
const file = fs.createReadStream('./message.txt', options);

file.on('open', (fd) => {
  console.log('開始讀取檔案');
});

file.on('data', (data) => {
  console.log('讀取到的資料:' + data);
});

file.on('end', () => {
  console.log('檔案已全部讀取完畢');
});

file.on('close', () => {
  console.log('檔案被關閉');
});

file.on('error', (err) => {
  console.log('讀取檔案失敗');
});

如下圖所示:

我們可以使用 ReadStream物件的pause方法暫停data事件的觸發,同時也意味著停止檔案的讀取操作。而已經被讀取到的作業系統快取區中的資料也將被暫時儲存在作業系統緩衝區中,在使用了pause方法暫停data事件的觸發之後,也可以使用ReadStream物件的resume方法恢復data事件的觸發,也就意味著可以繼續讀取檔案的資料。

如下demo:

const fs = require('fs');

const readStream = fs.createReadStream('./message.txt');

readStream.pause();

readStream.on('data', (data) => {
  console.log('獲取到的資料為:' +data);
});

setTimeout(() => {
  readStream.resume();
}, 1000);

讀取過程中,斷聽了一秒後,繼續把資料讀出來了。

注意:寫入檔案的方法是 WriteStream, 使用方式和讀方式類似,這裡不多介紹。

相關文章