Node.js Writable Stream:將數據寫入目標端
Writable Stream (可寫入串流) 是資料的終點,讓你能夠將數據塊 (Chunks) 寫入特定的目標,常見的例子包括寫入實體檔案 (fs.createWriteStream)、HTTP 回應物件 (res),或是標準輸出 process.stdout。
基礎寫入邏輯:write() 與 end()
使用 Writable Stream 時,核心流程是持續呼叫 write(),並在完成時呼叫 end() 釋放資源。
const fs = require('fs');
const writer = fs.createWriteStream('notes.txt');
// 寫入數據塊
writer.write('第一行日誌...\n');
writer.write('第二行數據...\n');
// 告知串流已完成,並可選擇性寫入最後一塊數據
writer.end('寫入結束!');
writer.on('finish', () => {
console.log('所有數據已成功寫入硬碟。');
});
進階概念:背壓 (Backpressure) 與 drain 事件
這是 Writable Stream 效能管理的靈魂。如果你的寫入速度快於底層儲存裝置(如硬碟)的處理速度,數據會不斷堆積在 Node.js 的內部緩衝區中。
- 當緩衝區爆滿(達到
highWaterMark)時,writer.write()會回傳false。 - 此時你應該停止寫入,並監聽
drain事件。 - 當
drain事件觸發時,代表緩衝區已清空,可以繼續餵資料。
function heavyWrite(writer, data) {
let i = 1000;
function write() {
let ok = true;
while (i > 0 && ok) {
i--;
if (i === 0) {
writer.end('最後一塊');
} else {
ok = writer.write(data); // 檢查緩衝區是否已滿
}
}
if (i > 0) {
// 緩衝區滿了,暫停一下,等 drain 再繼續
writer.once('drain', write);
}
}
write();
}
批次效能優化:cork() 與 uncork()
如果你需要連續寫入許多細小的資料塊,頻繁與作業系統通訊會拖慢效能。你可以使用 cork() 將寫入操作暫時快取在記憶體中,最後再一次性透過 uncork() 送出。
writer.cork();
writer.write('一部分...');
writer.write('另一部分...');
writer.write('更多部分...');
process.nextTick(() => writer.uncork());
核心事件總覽
drain:緩衝區清空,可恢復寫入。finish:呼叫end()且所有排隊數據皆已寫入完成。pipe:當有 Readable 接入時觸發。unpipe:當 Readable 離線時觸發。error:寫入過程發生任何異常。
專業提示:在大多數情況下,手動處理
write() 和 drain 是很繁瑣且容易出錯的。請盡量使用 pipe() 或 pipeline(),它們會自動幫你完美處理背壓問題。總結
- Writable 是資料的接收端。
- 務必檢查
write()的回傳值,以避免記憶體溢漏。 end()呼叫後,該串流將無法再次寫入。