node中的Buffer/fs/Stream的一些常用api

Linda_0504發表於2018-07-08

目錄

  • 1 Buffer

    • 1.1 概述
    • 1.2 常用api
  • 2 fs 模組

  • 2.1 fs概述

  • 2.2 常用api

  • 3 stream的概念以及分類

    • 3.1 stream的常用api
    • 3.2 stream的原理

二進位制快取區[Buffer]

概述

為了讓javascript能夠處理二進位制資料,node封裝了一個Buffer類,主要用於操作位元組,處理二進位制資料。Buffer 類的例項類似於整數陣列,但 Buffer 的大小是固定的、且在 V8 堆外分配實體記憶體。 Buffer 的大小在被建立時確定,且無法調整。

    // Buffer類是全域性變數,不需要require
    
    // 建立 buffer
    
     const buffer1 = new Buffer.alloc(10); // buffer1是一個長度為10 且用0填充的Buffer
     const buffer2 = new Buffer.alloc(10, 30); // buffer2是一個長度為10 且用30來填充的Buffer
     
     const buffer3 = new Buffer.allocUnsafe(10); // 建立一個長度為10 且未初始化的Buffer
    
    /** 建立一個包含 [0x1, 0x2, 0x3] 的 Buffer
     建立一個長度為 10、且未初始化的 Buffer。
     這個方法比呼叫 Buffer.alloc() 更快,
     但返回的 Buffer 例項可能包含舊資料,
     因此需要使用 fill() 或 write() 重寫。
     */
    const buffer4 = new Buffer.from([1, 2, 3]); 
    
    const buffer5 = new Buffer.from('tést'); // 建立一個包含 UTF-8 位元組 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。

    const buffer6 = new Buffer.from('tést', 'latin1'); // 建立一個包含 Latin-1 位元組 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。
複製程式碼

api

類方法以及屬性
  1. Buffer.alloc(size[, fill[, encoding]]): 分配一個大小為size位元組的新建的Buffer.fill如果為undefined,預設用 0 填充。 如果fill的值是字串,則encoding是fill的字元編碼格式,預設值為utf8。 size必須大於等於0,且小於buffer.constants.MAX_LENGTH,否則會報異常。
  2. Buffer.allocUnsafe(size): 分配一個大小為 size 位元組的新建的 Buffer 。
  3. Buffer.byteLength(string[, encoding]): 返回一個字串的實際位元組長度。
```
    const str = '\u00bd + \u00bc = \u00be';
    // 輸出: ½ + ¼ = ¾: 9 個字元, 12 個位元組
    console.log(`${str}: ${str.length} 個字元, ` +
            `${Buffer.byteLength(str, 'utf8')} 個位元組`);
```
複製程式碼
  1. Buffer.compare(buf1, buf2): 比較buf1和buf2的大小,相等返回0,小於返回-1,大於返回1。相當於呼叫 buf1.compare(buf2)。
  2. Buffer.concat(list[, totalLength]): 返回一個合併了 list 中所有 Buffer 例項的新建的 Buffer 。
```
        const buf1 = Buffer.alloc(10);
        const buf2 = Buffer.alloc(14);
        const buf3 = Buffer.alloc(18);
        const totalLength = buf1.length + buf2.length + buf3.length;
        console.log(totalLength); // 輸出: 42
        const bufA = Buffer.concat([buf1, buf2, buf3], totalLength); 
        console.log(bufA); // 輸出: <Buffer 00 00 00 00 ...>
        console.log(bufA.length); // 輸出: 42   
```
複製程式碼
  1. Buffer.from(array[| buffer | string[, encoding] | object[, offsetOrEncoding[, length]]]):

6.1 array: 通過一個八位位元組的 array 建立一個新的 Buffer 。如果 array 不是一個陣列,則丟擲 TypeError 錯誤。

  const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]);
複製程式碼
> 6.2 Buffer.from(buffer): 將傳入的 buffer 資料拷貝到一個新建的 Buffer 例項。
>
```
    const buf1 = Buffer.from('buffer');
    const buf2 = Buffer.from(buf1);
    buf1[0] = 0x61;
    console.log(buf1.toString());
    console.log(buf2.toString());
```  
> 6.3 Buffer.from(string[, encoding]): 新建一個包含所給的 JavaScript 字串 string 的 Buffer 。 encoding 引數指定 string 的字元編碼。
> 6.4 Buffer.from(object[, offsetOrEncoding[, length]])
>
複製程式碼
     class Foo {
        [Symbol.toPrimitive]() {
          return 'this is a test';
        }
      }
      const buf = Buffer.from(new Foo(), 'utf8');    
複製程式碼
  1. Buffer.isBuffer(obj): 如果 obj 是一個 Buffer 則返回 true ,否則返回 false 。
  2. Buffer.isEncoding(encoding): 如果 encoding 是一個支援的字元編碼則返回 true,否則返回 false 。
  3. Buffer.poolSize: 這是用於決定預分配的、內部 Buffer 例項池的大小的位元組數。 這個值可以修改。

例項屬性以及方法

  1. buf[index]: 索引操作符 [index] 可用於獲取或設定 buf 中指定 index 位置的八位位元組。
```
    const str = 'Node.js';
    const buf = Buffer.allocUnsafe(str.length); 
    for (let i = 0; i < str.length; i++) {
      buf[i] = str.charCodeAt(i);
    }   
    // 輸出: Node.js
    console.log(buf.toString('ascii'));
```
複製程式碼
  1. buf.buffer: buffer 屬性指向建立該 Buffer 的底層的 ArrayBuffer 物件
```
    const arrayBuffer = new ArrayBuffer(16);
    const buffer = Buffer.from(arrayBuffer);
    console.log(buffer.buffer === arrayBuffer);
    // 輸出: true
```
複製程式碼
  1. buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]]): 比較 buf 與 target,返回表明 buf 在排序上是否排在 target 之前、或之後、或相同。 對比是基於各自 Buffer 實際的位元組序列。如果 target 與 buf 相同,則返回 0 。如果 target 排在 buf 前面,則返回 1 。如果 target 排在 buf 後面,則返回 -1 。
  2. buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
```
    // 建立兩個 Buffer 例項 buf1 與 buf2 ,並拷貝 buf1 中第 16 個至第 19 個位元組到 buf2 第 8 個位元組起。
    const buf1 = Buffer.allocUnsafe(26);
    const buf2 = Buffer.allocUnsafe(26).fill('!');
    for (let i = 0; i < 26; i++) {
      // 97 是 'a' 的十進位制 ASCII 值
      buf1[i] = i + 97;
    }
    buf1.copy(buf2, 8, 16, 20);  
    // 輸出: !!!!!!!!qrst!!!!!!!!!!!!!
    console.log(buf2.toString('ascii', 0, 25));
```
複製程式碼
  1. buf.equals(otherBuffer): 如果 buf 與 otherBuffer 具有完全相同的位元組,則返回 true,否則返回 false。
  2. buf.fill(value[, offset[, end]][, encoding]): 如果未指定 offset 和 end,則填充整個 buf。 這個簡化使得一個 Buffer 的建立與填充可以在一行內完成。例如: Buffer.allocUnsafe(50).fill('h');
  1. buf.includes(value[, byteOffset][, encoding]): 返回: 如果 buf 找到 value,則返回 true,否則返回 false。
  2. buf.indexOf(value[, byteOffset][, encoding]): 返回: buf 中 value 首次出現的索引,如果 buf 沒包含 value 則返回 -1
  3. buf.keys(): 建立並返回一個包含 buf 鍵名(索引)的迭代器。
  4. buf.length: 返回 buf 在位元組數上分配的記憶體量。 注意,這並不一定反映 buf 內可用的資料量。
  5. buf.slice([start[, end]]): 返回一個指向相同原始記憶體的新建的 Buffer,但做了偏移且通過 start 和 end 索引進行裁剪。
  6. buf.toJSON(): 返回 buf 的 JSON 格式。 當字串化一個 Buffer 例項時,JSON.stringify() 會隱式地呼叫該函式。
  7. buf.toString([encoding[, start[, end]]]): 根據 encoding 指定的字元編碼解碼 buf 成一個字串。 start 和 end 可傳入用於只解碼 buf 的一部分。
  8. buf.values(): 建立並返回一個包含 buf 的值(位元組)的迭代器。 當 Buffer 使用 for..of 時會自動呼叫該函式。
  9. buf.write(string[, offset[, length]][, encoding]):根據 encoding 的字元編碼寫入 string 到 buf 中的 offset 位置。 length 引數是寫入的位元組數。 如果 buf 沒有足夠的空間儲存整個字串,則只會寫入 string 的一部分。 只部分解碼的字元不會被寫入。

fs模組

####1.1 概述

fs模組是檔案操作的封裝,繼承了EventEmitter、stream、path等底層模組,它提供了檔案讀取、寫入、更名、刪除、遍歷目錄、連結等POSIX檔案系統操作。與其它模組不同的是,fs模組中所有的操作都提供了非同步和同步的兩個版本。

檔案描述符

在 POSIX 系統,核心為所有程式維護著一張當前開啟著的檔案與資源的表格。 每個開啟的檔案都會分配一個名為檔案描述符的數值標識。 在系統層,所有檔案系統操作使用這些檔案描述符來識別與追蹤每個特定的檔案。 Window 系統使用了一個不同但概念類似的機制來追蹤資源。 為方便使用者,Node.js 抽象了不同作業系統間的差異,為所有開啟的檔案分配了數值的檔案描述符。fs.open() 方法用於分配一個新的檔案描述符。 一旦分配了,檔案描述符可用於讀取資料、寫入資料、或檢視檔案資訊。

    fs.open('/open/some/file.txt', 'r', (err, fd) => {
      if (err) throw err;
      fs.fstat(fd, (err, stat) => {
        if (err) throw err;
        // 各種操作
    
        // 必須關閉檔案描述符!
        fs.close(fd, (err) => {
          if (err) throw err;
        });
      });
    });
複製程式碼
fs.ReadStream 類

成功呼叫 fs.createReadStream() 會返回一個新的 fs.ReadStream 物件。 fs.ReadStream 物件都是可讀流。

  1. 監聽的事件有: close open rendy['open'之後立即出發]
  2. readStream.bytesRead: 已讀取的位元組數。
  3. readStream.path: 流正在讀取的檔案的路徑,指定在 fs.createReadStream() 的第一個引數。 如果 path 傳入的是字串,則 readStream.path 是一個字串。 如果 path 傳入的是 Buffer,則 readStream.path 是一個 Buffer。

fs.Stats 類

fs.Stats 物件提供了一個檔案的資訊。

  1. stats.isBlockDevice(): 如果 fs.Stats 物件表示一個塊裝置,則返回 true 。
  2. stats.isCharacterDevice(): 如果 fs.Stats 物件表示一個字元裝置,則返回 true 。
  3. stats.isDirectory(): 如果 fs.Stats 物件表示一個檔案系統目錄,則返回 true 。
  4. stats.isFIFO(): 如果 fs.Stats 物件表示一個先進先出(FIFO)管道,則返回 true 。
  5. stats.isFile(): 如果 fs.Stats 物件表示一個普通檔案,則返回 true 。
  6. stats.isSocket()
  7. stats.isSymbolicLink(): 如果 fs.Stats 物件表示一個符號連結,則返回 true 。
  8. stats上還有諸如dev/ino/mode/nlink/size等相關屬性。

fs.WriteStream 類

WriteStream 是一個可寫流。

  1. 監聽的事件有: close open ready
  2. writeStream.bytesWritten: 已寫入的位元組數。 不包括仍在排隊等待寫入的資料。
  3. writeStream.path: 流正在寫入的檔案的路徑,指定在 fs.createWriteStream() 的第一個引數。 如果 path 傳入的是一個字串,則 writeStream.path 是一個字串。 如果 path 傳入的是一個 Buffer,則 writeStream.path 是一個 Buffer。

fs上的方法

  1. fs.access(path[, mode], callback): 測試 path 指定的檔案或目錄的使用者許可權。 mode 引數是一個可選的整數,指定要執行的可訪問性檢查。 檔案訪問常量定義了 mode 可選的值。 可以建立由兩個或更多個值的位或組成的掩碼(例如 fs.constants.W_OK | fs.constants.R_OK)。最後一個引數 callback 是一個回撥函式,會傳入一個可能的錯誤引數。 如果可訪問性檢查失敗,則錯誤引數會是一個 Error 物件。
    ```
        const file = 'package.json';
        // 檢查檔案是否存在於當前目錄,且是否可寫。
        fs.access(file, fs.constants.F_OK | fs.constants.W_OK, (err) => {
     if (err) {
            console.error(
              `${file} ${err.code === 'ENOENT' ? '不存在' : '只可讀'}`);
          } else {
            console.log(`${file} 存在,且可寫`);
          }
        });
    ```
複製程式碼
  1. fs.accessSync(path[, mode]): 同步地測試 path 指定的檔案或目錄的使用者許可權。
```
    try {
        fs.accessSync('etc/passwd', fs.constants.R_OK | fs.constants.W_OK);
        console.log('可讀可寫');
    } catch (err) {
        console.error('不可訪問!');
    }
```  
複製程式碼
  1. fs.appendFile(path, data[, options], callback):非同步地追加資料到一個檔案,如果檔案不存在則建立檔案。 data 可以是一個字串或 Buffer。注意 file 可能是一個被開啟用來追加資料的數字檔案描述符(通過 fs.open() 或者 fs.openSync())。這樣的檔案描述符將不會被自動關閉。
```
    引數
    file <string> | <Buffer> | <URL> | <number> 檔名或檔案描述符
    data <string> | <Buffer>
    options <Object> | <string>
    encoding <string> | <null> 預設為 'utf8'
    mode <integer> 預設為 0o666
    flag <string> 預設為 'a'
    callback <Function>
    err <Error>
    ----------------------------------------------------------------- 
    fs.open('message.txt', 'a', (err, fd) => {
        if (err) throw err;
        fs.appendFile(fd, 'data to append', 'utf8', (err) => {
        fs.close(fd, (err) => {
            if (err) throw err;
        });
        if (err) throw err;
  });
});
```    
複製程式碼
  1. fs.appendFileSync(path, data[, options]): 同步的將data追加到path對應得檔案中。
  2. fs.chmod(path, mode, callback): 非同步地改變檔案的許可權。 完成回撥只有一個可能的異常引數。
  3. fs.chmodSync(path, mode): 同步地改變檔案的許可權。 返回 undefined。 fs.chmod()的同步版本。
  4. fs.chown(path, uid, gid, callback): 非同步地改變檔案的所有者和群組。 完成回撥只有一個可能的異常引數。
  5. fs.chownSync(path, uid, gid): 同步地改變檔案的所有者和群組。 返回 undefined。 fs.chown() 的同步版本。
  6. fs.close(fd, callback):非同步的關閉檔案。 完成回撥只有一個可能的異常引數。
  7. fs.closeSync(fd)
  8. fs.copyFile(src, dest[, flags], callback): 非同步的將 src 拷貝到 dest。Asynchronously copies src to dest. 預設情況下,如果 dest 已經存在會被覆蓋。回撥函式沒有給出除了異常以外的引數。Node.js 不能保證拷貝操作的原子性。如果目標檔案開啟後出現錯誤,Node.js 將嘗試刪除它。flags 是一個可選的整數,用於指定行為的拷貝操作。唯一支援的 flag 是 fs.constants.COPYFILE_EXCL ,如果 dest 已經存在,則會導致拷貝操作失敗。
  9. fs.copyFileSync(src, dest[, flags]): 同步的將 src 拷貝到 dest。
  10. fs.createReadStream(path[, options]):
```
    path <string> | <Buffer> | <URL>
    options <string> | <Object>
    flags <string> 詳見支援的檔案系統flag。預設為 'r'。
    encoding <string> 預設為 null。
    fd <integer> 預設為 null。
    mode <integer> 預設為 0o666。
    autoClose <boolean> 預設為 true。
    start <integer>
    end <integer> 預設為 Infinity。
    highWaterMark <integer> 預設為 64 * 1024。
```
返回: <fs.ReadStream> 詳見可讀流。
不同於 highWaterMark 預設值為 16 kb 的可讀流,該方法返回的流的 highWaterMark 預設為 64 kb。
options 可以包括 start 和 end 值,用於從檔案讀取一定範圍的位元組而不是整個檔案。 start 和 end 都是包括在內的,且從 0 開始。 如果指定了 fd 且 start 不傳或為 undefined,則 fs.createReadStream() 從當前檔案位置按順序地讀取。 encoding 可以是任何可以被 Buffer 接受的值。
如果指定了 fd,則 ReadStream 會忽略 path 引數並且會使用指定的檔案描述符。 這意味著不會觸發 'open' 事件。 注意,fd 必須是阻塞的,非阻塞的 fd 應該傳給 net.Socket。
 如果 autoClose 為 false,則檔案描述符不會被關閉,即使有錯誤。 應用程式需要負責關閉它,並且確保沒有檔案描述符洩漏。 如果 autoClose 被設定為 true(預設),則在 error 或 end 時,檔案描述符會被自動關閉。
mode 用於設定檔案模式(許可權和粘滯位),但僅限建立檔案時。 
複製程式碼
  1. fs.createWriteStream(path[, options])
```
    path <string> | <Buffer> | <URL>
    options <string> | <Object>
    flags <string> 詳見支援的檔案系統flag。預設為 'w'。
    encoding <string> 預設為 'utf8'。
    fd <integer> 預設為 null。
    mode <integer> 預設為 0o666。
    autoClose <boolean> 預設為 true。
    start <integer>
```
返回: <fs.WriteStream> 詳見可寫流。
options 也有 start 選項,用於寫入資料到檔案指定位置。 如果是修改檔案而不是覆蓋它,則 flags 模式需為 r+ 模式而不是預設的 w 模式。 encoding 可以是任何可以被 Buffer 接受的值。
如果 autoClose 被設定為 true(預設),則在 error 或 finish 時,檔案描述符會被自動關閉。 如果 autoClose 為 false,則檔案描述符不會被關閉,即使有錯誤。 應用程式需要負責關閉它,並且確保沒有檔案描述符洩漏。
類似 ReadStream,如果指定了 fd,則 WriteStream 會忽略 path 引數並且會使用指定的檔案描述符。 這意味著不會觸發 'open' 事件。 注意,fd 必須是阻塞的,非阻塞的 fd 應該傳給 net.Socket。
如果 options 是一個字串,則它指定字元編碼。 
複製程式碼
  1. fs.existsSync(path): 如果路徑存在,則返回 true,否則返回 false。
  2. fs.open(path, flags[, mode], callback)與fs.openSync(path, flags[, mode]): 非同步或者同步地開啟檔案
  3. fs.read(fd, buffer, offset, length, position, callback)與 fs.readSync(fd, buffer, offset, length, position): 從 fd 指定的檔案中讀取資料。
  4. fs.readdir(path[, options], callback):讀取一個目錄的內容。 回撥有兩個引數 (err, files),其中 files 是目錄中不包括 '.' 和 '..' 的檔名的陣列。
  5. fs.readdirSync(path[, options])
  6. fs.readFile(path[, options], callback): 非同步地讀取一個檔案的全部內容
  7. fs.readFileSync(path[, options])
  8. fs.rename(oldPath, newPath, callback)與 fs.renameSync(oldPath, newPath): 修改檔名稱
  9. fs.rmdir(path, callback)與 fs.rmdirSync(path): 刪除檔案目錄
  10. fs.write與fs.writeFile

實在很抱歉,本來是要寫一些原理性的東西,無奈,fs和buffer的基礎方法和屬性實在太多了,所以只能先按照官網上說了,先過一遍基礎知識了。。。但我相信掌握基礎的api還是是有很必要的,爭取下次寫點看著高階的東西

pkq
皮卡丘

Stream

概述

  • 流是一組有序的,有起點和終點的位元組資料傳輸手段 它不關心檔案的整體內容,只關注是否從檔案中讀到了資料,以及讀到資料之後的處理
  • 流是一個抽象介面,被 Node 中的很多物件所實現。比如HTTP 伺服器request和response物件都是流。
  • 可讀流和可寫流對檔案的操作用的也是fs模組
  • node中很多內容都應用到了流,比如http模組的req就是可讀流,res是可寫流,而socket是可讀可寫流,看起來很膩害的樣子~

流的分類

  • stream.Readable---用於在I/O上獲取資料,如 fs.createWriteStream()

  • stream.Writable---用於在輸出的目標寫入資料, 如 fs.createReadStream()

  • stream.Duplex---一個可讀可寫的流,例如網路連線,如net.Socket

  • stream.Transform---一個會以某種方式修改資料的雙工流,如 zlib.createDeflate()

緩衝區

  • 可寫流和可讀流都會在一個內部的緩衝器中儲存資料,可以分別使用的 writable.writableBuffer 或 readable.readableBuffer 來獲取。
  • 可緩衝的資料的數量取決於傳入流建構函式的 highWaterMark 選項。 對於普通的流,highWaterMark 表示位元組的總數量。 對於以物件模式運作的流,highWaterMark 指定了物件的總數量。
  • 當呼叫 stream.push(chunk) 時,資料會被緩衝在可讀流中。 如果流的消費程式沒有呼叫 stream.read(),則這些資料會停留在內部佇列中,直到被消費。一旦內部的可讀緩衝的總大小達到 highWaterMark 指定的閾值時,流會暫時停止從底層資源讀取資料,直到當前緩衝的資料被消費 (也就是說,流會停止呼叫內部的用於填充可讀緩衝的 readable._read() 方法)。
  • 當反覆地呼叫 writable.write(chunk) 方法時,資料會被緩衝在可寫流中。 當內部的可寫緩衝的總大小小於 highWaterMark 設定的閾值時,呼叫 writable.write() 會返回 true。 一旦內部緩衝的大小達到或超過 highWaterMark 時,則會返回 false。
  • stream API 的主要目標,特別是 stream.pipe() 方法,是為了限制資料的緩衝到可接受的程度,也就是讀寫速度不一致的源頭與目的地不會壓垮可用的記憶體。
  • 因為 Duplex 和 Transform 都是可讀又可寫的,所以它們各自維護著兩個相互獨立的內部緩衝器用於讀取和寫入, 這使得它們在維護資料流時,讀取和寫入兩邊可以各自獨立地運作。

stream.Writable 類

常用事件
  • 'close' 事件: 當流或其底層資源(比如檔案描述符)被關閉時,觸發 'close' 事件。 該事件表明不會再觸發其他事件,且不會再發生運算。
  • 'drain'事件: 當一個流不處在 drain 的狀態, 對 write() 的呼叫會快取資料塊, 並且返回 false。 一旦所有當前所有快取的資料塊都排空了(被作業系統接受來進行輸出), 那麼 'drain' 事件就會被觸發 建議, 一旦 write() 返回 false, 在 'drain' 事件觸發前, 不能寫入任何資料塊
  • 'error' 事件:當寫入資料出錯或使用管道出錯時,觸發 'error' 事件。 監聽器回撥函式被呼叫時會傳入一個 Error 引數。但觸發 'error' 事件時,流還未被關閉。
  • 'pipe' 事件: 當在可讀流上呼叫 stream.pipe() 方法新增可寫流到目標流向時,觸發 'pipe' 事件。
  • 'unpipe' 事件: 當在可讀流上呼叫 stream.unpipe() 方法從目標流向中移除當前可寫流時,觸發 'unpipe' 事件。當可讀流通過管道流向可寫流發生錯誤時,也會觸發 'unpipe' 事件。
例項方法與屬性

writable: stream.Writable例項

  • writable.destroy([error]):摧毀這個流,併發出傳過來的錯誤。當這個函式被呼叫後,這個寫入流就結束了。
  • writable.end([chunk][, encoding][, callback]):
chunk <string> | <Buffer> | <Uint8Array> | <any> 可選的,需要寫入的資料。對於非物件模式下的流, chunk 必須是字串、或 Buffer、或 Uint8Array。對於物件模式下的流, chunk 可以是任意的 JavaScript 值,除了 null。
encoding <string> 如果 chunk 是字串,這裡指定字元編碼。
callback <Function> 可選的,流結束時的回撥函式。
複製程式碼
呼叫 writable.end() 方法表明接下來沒有資料要被寫入可寫流。通過傳入可選的 chunk 和 encoding 引數,可以在關閉流之前再寫入一段資料。如果傳入了可選的 callback 函式,它將作為 'finish' 事件的回撥函式。
複製程式碼
  • writable.setDefaultEncoding(encoding):writable.setDefaultEncoding() 用於為可寫流設定 encoding。
  • writable.writableHighWaterMark: 返回構造該可寫流時傳入的 highWaterMark 引數值。
  • writable.writableLength:這個屬性包含了寫入就緒佇列的位元組(或者物件)數
  • writable.write(chunk[, encoding][, callback])
    chunk <string> | <Buffer> | <Uint8Array> | <any>
    encoding <string> 如果 chunk 是字串,這裡指定字元編碼。
    callback <Function> 緩衝資料輸出時的回撥函式。
    返回: <boolean> 如果流需要等待 'drain' 事件觸發才能繼續寫入資料,這裡將返回 false ; 否則返回 true複製程式碼

writable.write() 方法向流中寫入資料,並在資料處理完成後呼叫 callback 。如果有錯誤發生, callback 不一定 以這個錯誤作為第一個引數並被呼叫。要確保可靠地檢測到寫入錯誤,應該監聽 'error' 事件。

在確認了 chunk 後,如果內部緩衝區的大小小於建立流時設定的 highWaterMark 閾值,函式將返回 true 。 如果返回值為 false ,應該停止向流中寫入資料,直到 'drain' 事件被觸發。 當一個流不處在 drain 的狀態, 對 write() 的呼叫會快取資料塊, 並且返回 false。 一旦所有當前所有快取的資料塊都排空了(被作業系統接受來進行輸出), 那麼 'drain' 事件就會被觸發。 我們建議, 一旦 write() 返回 false, 在 'drain' 事件觸發前, 不能寫入任何資料塊。 然而,當流不處在 'drain' 狀態時, 呼叫 write() 是被允許的, Node.js 會快取所有已經寫入的資料塊, 直到達到最大記憶體佔用, 這時它會無條件中止。 甚至在它中止之前, 高記憶體佔用將會導致差的垃圾回收器的效能和高的系統相對敏感性 (即使記憶體不再需要,也通常不會被釋放回系統)。 對於一個 Transform, 寫入資料到一個不會drain的流尤其成問題, 因為 Transform 流預設被暫停, 直到它們被pipe或者被新增了 'data' 或 'readable' 事件處理函式。

可讀流的應用

客戶端上的 HTTP 請求 伺服器上的 HTTP 響應 fs 寫入的流 zlib 流 crypto 流 TCP socket 子程式 stdin process.stdout、process.stderr

可讀流

兩種模式

可讀流實質上運作於流動中(flowing)或已暫停(paused)兩種模式之一。

  • 在 flowing 模式中,資料自動地從底層的系統被讀取,並通過 EventEmitter 介面的事件儘可能快地被提供給應用程式。

  • 在 paused 模式中,必須顯式呼叫 stream.read() 方法來從流中讀取資料片段。

所有可讀流都開始於 paused 模式。 paused To flowing:

  • 新增一個 'data' 事件處理函式。
  • 呼叫 stream.resume() 方法。
  • 呼叫 stream.pipe() 方法傳送資料到可寫流。

flowing To paused:

  • 如果沒有管道目標,呼叫 stream.pause() 方法。
  • 如果有管道目標,移除所有管道目標。呼叫 stream.unpipe() 方法可以移除多個管道目標。
三種模式

在任意時刻,任一可讀流會處於以下三種狀態之一:

  • readable.readableFlowing = null
  • readable.readableFlowing = false
  • readable.readableFlowing = true

當 readable.readableFlowing 為 null 時,沒有提供消費流資料的機制,所以流不會產生資料。 在這個狀態下,監聽 'data' 事件、呼叫 readable.pipe() 方法、或呼叫 readable.resume() 方法, 則 readable.readableFlowing 會變成 true ,可讀流開始主動地產生資料觸發事件。

呼叫 readable.pause()、readable.unpipe()、或接收背壓,則 readable.readableFlowing 會被設為 false,暫時停止事件流動但不會停止資料的生成。 在這個狀態下,為 'data' 事件設定監聽器不會使 readable.readableFlowing 變成 true。

事件
  • 'close' 事件:'close' 事件將在流或其底層資源(比如一個檔案)關閉後觸發。'close' 事件觸發後,該流將不會再觸發任何事件。

  • 'data' 事件: 'data' 事件會在流將資料傳遞給消費者時觸發。當流轉換到 flowing 模式時會觸發該事件。呼叫 readable.pipe(), readable.resume() 方法,或為 'data' 事件新增回撥可以將流轉換到 flowing 模式。 'data' 事件也會在呼叫 readable.read() 方法並有資料返回時觸發。如果呼叫 readable.setEncoding() 方法明確為流指定了預設編碼,回撥函式將接收到一個字串,否則接收到的資料將是一個 Buffer 例項。

        const readable = getReadableStreamSomehow();
        readable.on('data', (chunk) => {
          console.log(`Received ${chunk.length} bytes of data.`);
        });
    複製程式碼

'end' 事件:'end' 事件將在流中再沒有資料可供消費時觸發。

  • 'error' 事件: 通常,這會在底層系統內部出錯從而不能產生資料,或當流的實現試圖傳遞錯誤資料時發生。
  • 'readable' 事件: 'readable' 事件將在流中有資料可供讀取時觸發。在某些情況下,為 'readable' 事件新增回撥將會導致一些資料被讀取到內部快取中。通常情況下,readable.pipe() 方法和 'data' 事件機制比 'readable' 事件更容易理解。然而處理 'readable'事件可能造成吞吐量升高。
例項方法與屬性
  • readable.destroy([error]):銷燬流,並且觸發error事件。然後,可讀流將釋放所有的內部資源。

  • readable.isPaused():readable.isPaused() 方法返回可讀流的當前操作狀態。

  • readable.pause(): readable.pause() 方法將會使 flowing 模式的流停止觸發 'data' 事件, 進而切出 flowing 模式。任何可用的資料都將儲存在內部快取中。

        const readable = getReadableStreamSomehow();
        readable.on('data', (chunk) => {
          console.log(`Received ${chunk.length} bytes of data.`);
          readable.pause();
          console.log('There will be no additional data for 1 second.');
          setTimeout(() => {
            console.log('Now data will start flowing again.');
            readable.resume();
          }, 1000);
        });
    複製程式碼
  • readable.pipe(destination[, options]): readable.pipe() 繫結一個 [Writable][] 到 readable 上, 將可寫流自動切換到 flowing 模式並將所有資料傳給繫結的 [Writable][]。資料流將被自動管理。這樣,即使是可讀流較快,目標可寫流也不會超負荷(overwhelmed)。

    destination <stream.Writable> 資料寫入目標
    options <Object> Pipe 選項
        end <boolean> 在 reader 結束時結束 writer 。預設為 true。
        
    const readable = getReadableStreamSomehow();
    const writable = fs.createWriteStream('file.txt');
    複製程式碼

// readable 中的所有資料都傳給了 'file.txt' readable.pipe(writable);

```
複製程式碼
  • readable.read([size]):從內部緩衝區中抽出並返回一些資料。 如果沒有可讀的資料,返回null。readable.read()方法預設資料將作為“Buffer”物件返回 ,除非已經使用readable.setEncoding()方法設定編碼或流執行在物件模式。可選的size引數指定要讀取的特定數量的位元組。如果size位元組不可讀,將返回null除非流已經結束,在這種情況下所有保留在內部緩衝區的資料將被返回。如果沒有指定size引數,則內部緩衝區包含的所有資料將返回。
  • readable.readableHighWaterMark: 返回構造該可讀流時傳入的 'highWaterMark' 屬性。
  • readable.resume(): readable.resume() 方法會重新觸發 'data' 事件, 將暫停模式切換到流動模式。
  • readable.setEncoding(encoding): 從可讀流讀入的資料設定字元編碼
  • readable.unshift(chunk):readable.unshift() 方法會把一塊資料壓回到Buffer內部。 這在如下特定情形下有用: 程式碼正在消費一個資料流,已經"樂觀地"拉取了資料。 又需要"反悔-消費"一些資料,以便這些資料可以傳給其他人用。
可讀流的應用

客戶端上的 HTTP 響應 伺服器上的 HTTP 請求 fs 讀取的流 zlib 流 crypto 流 TCP socket 子程式 stdout 與 stderr process.stdin 所有的可讀流都實現了 stream.Readable 類上定義的介面。

艾瑪,終於弄完了,估計師傅看了,又該說我low了,?

pkq
皮卡丘

相關文章