Node 中的流(Stream)

廢話

樸(piáo)老溼的九淺一深 Node.js 貌似沒有詳細介紹 Stream 模塊。本身對這個模塊算是比較熟悉,順手也 PO 上來備忘一下吧。html

又是廢話

若是說 Activity、Service、Content Provider 和 Broadcast Receiver 並稱 Android 四大組件的話,那麼 Node 的「N 大模塊」裏面一定要有 Stream。node

相似於 *nix 將幾乎全部設備抽象爲文件同樣,Node 將幾乎全部 IO 操做都抽象成了 Stream 的操做。Stream 是一個抽象的概念,總之就是會冒數據(以 Buffer 爲單位),或者可以吸取數據的東西。git

爲何須要 Stream 呢?

在 Node 裏讀取一個文件的內容:github

var fs = require('fs')

fs.readFile('/etc/hosts', function (err, buffer) {
  if (err) {
    return console.error(err.stack)
  }

  console.log(buffer.toString('utf8'))
})

對於大文件操做,一口氣所有讀入內存確定是不行的,科學的作法是一份一份地讀:api

var fs = require('fs')

fs.open('/etc/hosts', 'r', function (err, fd) {
  if (err) {
    return console.error(err.stack)
  }

  // 內啥,我想演示一下用 fs.read 逐片逐片讀文件的,但那玩意實在太煩,
  // 因此我懶得寫了。本身看文檔。
})

很煩對吧?每次讀多少?這些每每不是咱們須要關心的,因而就有了 Stream。Node 對這種傳統的讀取過程隱藏到了文件讀取 Stream 中:網絡

var fs = require('fs')
fs.createReadStream('/etc/hosts').pipe(process.stdout)

沒錯,就兩行代碼,將 hosts 文件的內容顯示出來。dom

之前的 Stream

在 Node 的早期版本中,Stream 只是一個簡單的繼承自 EventEmitter 的類。以文件讀取流爲例子,一旦開始讀取,數據就會源源不斷地讀出,觸發 'data' 事件——嗯一開始的確是一個不錯的設計,但若是你想等一下,好比說等待某個回調後再開始處理這些數據。。。好吧,每一片數據只會冒出來一次,沒人消費的話它就沒了;OK 你想到了用 pause-stream「暫停」一下這個流,等下再消費?完了,數據會暫存在內存中,慢慢堆積,而後,你懂了。ide

如今的 Stream

固然並非說原來不斷冒 'data' 的讀取方式是很差的,只是說它對於某些情景,好比文件讀取,好比網絡傳輸,沒辦法控制讀取速度而已。網絡傳輸註定是沒辦法控制讀取速度的,人家給你多少你都得吃進去;但文件讀取,能不能按須要,處理多少讀多少呢?post

Node 0.10.X 就對 Stream 作了這樣的改動:默認狀況下,Node 會盡量使用「拉」模式,也就是說只有 pipe 鏈末端的流消費者真正須要數據的時候,數據纔會從源頭被取出,而後順着管子一路到達消費者。ui

這樣我就能夠把管子插……

Stream 的精髓在於 .pipe() 嗯。

下載文件?So easy

推薦一個牛逼哄哄的庫:request,Stream API 運用到極致的 HTTP(S) 請求庫。

var fs = require('fs')
  , request = require('request')

var writer = fs.createWriteStream('hexie.pkg')
// 'close' 和 'error' 事件分別是寫入完成和發生錯誤,懶得寫,看文檔。

request('http://xxxxxxx.xxx/abs123.avi').pipe(writer)

fs.createWriteStream() 建立了一個新文件並返回了該文件的 Writable Stream;request() 發起了一個 HTTP 請求並返回一個 Readable Stream。將這兩個 Stream 經過 .pipe() 一對接,request() 的下載流量就被直接導到本地文件並寫入磁盤,神奇吧?

模擬 HTML POST 表單上傳也是 So easy

推薦一下樸老溼好基友蘇千大神(@Python發燒友)的 formstream,配合 request 簡直碉堡。

var request = require('request')
  , FormStream = require('formstream')

var form = FormStream()
  .field('title', 'ni dong de')
  .file('attachment', 'hexie.pkg')

var upload = request.post('http://xxxx.xxx/upload', {
  headers: form.headers()
}, function (err, res, body) {
  // ... 略
})

form.pipe(upload)

最後,仍是廢話

最後,最後,還得感謝老雷(@雷宗民)的 Node.js API 翻譯計劃,俺就是翻譯完 Stream 章節才理解上面這些精髓的。

以上。

相關文章
相關標籤/搜索