Stream(流)的基本使用

目錄

image

stream 是一組有序的,有起點和終點的字節數據傳輸手段,並且有不錯的效率。 藉助事件和非阻塞I/O庫,流模塊容許在其可用的時候動態處理,在其不須要的時候釋放掉。
stream是一種在Node.js中處理流式數據的抽象接口。stream 模塊提供瞭如下基礎的API,用於構建實現了流接口對象。 流能夠是可讀、可寫、或是可讀寫的。git

基本使用

可讀流

在NodeJS中,咱們對文件的操做須要依賴核心模塊 fsfs 模塊中集成了 createReadStream 可讀流。github

fs.createReadStream(path, options) 參數以下:緩存

  • path : 讀取的文件路徑bash

  • options: string(指定字符編碼) | Object(下面詳細介紹)模塊化

    • flags : 標識位,默認爲 'r'
    • encoding : 字符編碼, 默認爲null,讀取到的值爲Buffer
    • fd :文件描述符,默認爲 null;
    • mode :權限位,默認爲 0o666;
    • autoClose : 讀取完後是否自動關閉,默認爲true
    • start : 開始讀取位置,默認0
    • end : 結束位置,默認文章結束
    • highWaterMark :// 最高水位線,每次讀多少個。 默認:64*1024 字節
  • 返回值爲:fs.ReadStream函數

建立可讀流

// 引入依賴
let fs = require('fs')
let rs = fs.createReadStream('./readStream.txt', {
  // start: 0, // 開始讀取位置,默認0
  end: 3, // 結束位置,默認文章讀取完
  highWaterMark: 3, // 最多讀取, 每次讀取的個數 默認:64*1024 字節
})
複製代碼

可讀流中的事件機制

在下面例子中 ./readStream.txt 的文件內容爲: 1234567890post

1. open

open 事件用來監聽文件的打開,回調函數在打開文件後執行。測試

// 引入依賴
let fs = require('fs')
let rs = fs.createReadStream('./readStream.txt', {
  start: 0,
  end: 3,
  highWaterMark: 3
})
// 事件機制,須要本身去監聽一些數據
// open 文件打開
rs.on('open', () => {
  console.log('文件開啓了')
})
// 結果:文件開啓了
複製代碼
2. data

data 事件,每次讀取 highWaterMark 個字節,就觸發一次該事件,直到讀取完成,回調函數裏面返回的時每次讀取的結果。若是encoding 不設置,則返回爲Bufferui

// 引入依賴
let fs = require('fs')
let rs = fs.createReadStream('./readStream.txt', {
  // encoding: 'utf8',
  start: 0,
  end: 3,
  highWaterMark: 3
})
// 事件機制,須要本身去監聽一些數據
// open 文件打開
rs.on('open', () => {
  console.log('文件開啓了')
})
rs.on('data', (data) => {
  console.log(data)
})
// 若是不是設置encoding返回結果爲
/** 文件開啓了
 *<Buffer 31 32 33>
 * <Buffer 34>
 */
 
// 若是encoding: 'utf8'返回結果爲
/** 文件開啓了
 * 123
 * 4
 */
複製代碼
3. end

end 事件,當文件讀取完觸發,並執行回調。編碼

讀取完狀況以下:

  1. end 設置了值( end: 3 ),則讀完設置的長度觸發
  2. end 沒有設置值,默認讀取完全部內容觸發。

爲了方便查看結果,在下面例子中,咱們也實現了這兩種狀況:

// 引入依賴
let fs = require('fs')
let rs = fs.createReadStream('./readStream.txt', {
  start: 0,
  // end: 3,
  highWaterMark: 3
})
// 事件機制,須要本身去監聽一些數據
// open 文件打開
rs.on('open', () => {
  console.log('文件開啓了')
})
rs.on('data', (data) => {
  console.log(data)
})
rs.on('end', () => {
  console.log('結束了')
})
// 設置了end: 3的運行結果
/** 文件開啓了
 * <Buffer 31 32 33>
 * <Buffer 34>
 * 結束了
 */
 
// 沒有設置end參數的運行結果
/** 文件開啓了
 * <Buffer 31 32 33>
 * <Buffer 34 35 36>
 * <Buffer 37 38 39>
 * <Buffer 30>
 * 結束了
 */
複製代碼
4. error

error 事件監聽錯誤信息,在讀文件出差時觸發回調,並將錯誤信息返回

// error事件 出錯時自動調用
rs.on('error', (err) => {
  console.log(err)
})
複製代碼
5. close

close 事件用來監聽文件的關閉,回調函數在文件關閉後執行。在建立可讀流時,autoClose: true(默認值爲true),自動關閉文件,且觸發 close 事件執行回調。

rs.on('close', () => {
  console.log('close')
})
複製代碼

暫停和恢復

可讀流有兩種狀態:流動狀態或暫停狀態兩種。

可讀流開始都時暫停狀態,能夠經過如下方式切換到流動狀態:

  • 監聽 data 事件
  • 調用 fs.resume() 方法

可讀流能夠經過如下方式切換回暫停狀態:

  • 調用 rs.pause() 方法

什麼狀況先咱們會用到暫停和恢復呢?
  咱們都知道讀取文件會佔用內容空間,若是咱們遇到特別大的文件讀取時,若是所有讀出會佔用很大的內容空間,這不是咱們想要看到的,咱們想到讀取一部分數據,處理完成後再次讀取,就會用到了。

一個簡單的例子

// 引入依賴
let fs = require('fs')
let rs = fs.createReadStream('./readStream.txt', {
  encoding: 'utf8', // 字符編碼, 默認爲null
  highWaterMark: 2
})
let i = 0

rs.on('data', (data) => {
  i ++
  console.log(`第 ${i} 次`, new Date());
  console.log(data)
  rs.pause() // 暫停
  setTimeout(() => {
    rs.resume() // 恢復
  }, 1000)
})
rs.on('end', () => {
  console.log('結束了')
})
// 第 1 次 2018-09-16T10:11:00.993Z
// 12
// 第 2 次 2018-09-16T10:11:01.996Z
// 34
// 第 3 次 2018-09-16T10:11:02.997Z
// 56
// 第 4 次 2018-09-16T10:11:03.997Z
// 78
// 第 5 次 2018-09-16T10:11:04.998Z
// 90
// 結束了
複製代碼

可寫流

在NodeJS中,咱們對文件的操做須要依賴核心模塊 fsfs 模塊中集成了 createWriteStream 可讀流

fs.createWriteStream(path, options) 參數以下:

  • path : 讀取的文件路徑

  • options: string(指定字符編碼) | Object

    • flags : 標識位,默認爲 'w'
    • encoding : 字符編碼, 默認爲 utf8
    • fd :文件描述符,默認爲 null;
    • mode :權限位,默認爲 0o666;
    • autoClose : 是否自動關閉,默認爲true
    • start : 開始讀取位置,默認0
    • highWaterMark : 寫入個數
  • 返回值爲:fs.WriteStream

建立可寫流

writeStream.txt文件,表示要寫入內容的文件

// 引入依賴
let fs = require('fs')
let ws = fs.createWriteStream('./writeStream.txt', {
  start: 0, // 開始讀取位置,默認0
})
複製代碼

寫內容和結束

write在對應的文件寫入內容。end表示寫入結束,若是後面參數有內容,也是能夠入去到文件。

// 引入依賴
let fs = require('fs')
let ws = fs.createWriteStream('./writeStream.txt', {
  start: 0, // 開始寫入位置,默認0
})
ws.write('1') // 寫內容
ws.end('寫完了') // 結束

//結果爲: 1寫完了
複製代碼

可寫流中的事件機制

可寫流裏面的事件 openclose相對對簡單再也不詳述。主要說一下 finishdrain 事件.

1. finish 事件

當調用 ws.end() 方法且緩衝數據都已經傳給底層系統以後,觸發 'finish' 事件。

測試代碼以下:

// 引入依賴
let fs = require('fs')
let ws = fs.createWriteStream('./writeStream.txt', {
  start: 0, // 開始寫入位置,默認0
})
ws.on('open', () => {
  console.log('open');
});
ws.write('1')
ws.end('寫完了')
ws.on('finish', () => {
  console.log('全部寫入已完成。');
});
ws.on('close', () => {
  console.error('close');
});

// 輸出結果爲:
// open
// 全部寫入已完成。
// close
複製代碼
2. drain 事件

若是調用 ws.write(chunk) 方法返回 false,也就是寫入的內容到達緩存區的大小,觸發 drain事件

let fs = require('fs')
let ws = fs.createdWriteStream('writeStream.txt', {
    start: 0,
    highWaterMark: 5
})

// 寫入15個數,每次寫五個,只但願佔用五個字節的內存
let i = 0
function write() {
    let flag = true
    while(i < 15 && flag) {
        flag = ws.write(i++'','utf8')
    }
}
write() // 若是寫入的內容到達緩存區的大小了,當他寫入完成後會觸發一個事件
ws.on('drain', () => {
    console.log('佔滿')
    write() //清空緩存 繼續寫入
})

// 佔滿
// 佔滿
複製代碼

可讀可寫混合使用

經過 pipe 方式讀取一個文件內容,寫入到另一個文件【經常使用】

// 引入fs模塊
const fs = require("fs");

// 建立可讀流和可寫流
let rs = fs.createReadStream("./ReadStream/readStream.txt", {
    highWaterMark: 3
});
let ws = fs.createWriteStream("./writeStream.txt", {
    highWaterMark: 2
});

// 將 readStream.txt 的內容經過流寫入 writeStream.txt 中
rs.pipe(ws);
複製代碼

附源碼

重要爲了方便你們瞭解、查看、調試代碼,完整的源碼參見gitHub

總結

本篇文章是 NodeJs 中流(stream)的基礎瞭解。但願對你們瞭解流起到必定的做用。

下篇NodeJS 流的實現原理和簡單實現—— 《流之原,源之理》

做者:香香

未來的你,必定會感謝如今拼命努力的本身!

Node基礎系列文章

相關文章
相關標籤/搜索