nodejs中流(stream)的理解
nodejs的fs模組並沒有提供一個copy的方法,但我們可以很容易的實現一個,比如:
[JavaScript] 純文字檢視 複製程式碼var source = fs.readFileSync('/path/to/source', {encoding: 'utf8'}); fs.writeFileSync('/path/to/dest', source);
這種方式是把檔案內容全部讀入記憶體,然後再寫入檔案,對於小型的文字檔案,這沒有多大問題,比如grunt-file-copy就是這樣實現的。但是對於體積較大的二進位制檔案,比如音訊、視訊檔案,動輒幾個GB大小,如果使用這種方法,很容易使記憶體“爆倉”。理想的方法應該是讀一部分,寫一部分,不管檔案有多大,只要時間允許,總會處理完成,這裡就需要用到流的概念。
如上面高大上的圖片所示,我們把檔案比作裝水的桶,而水就是檔案裡的內容,我們用一根管子(pipe)連線兩個桶使得水從一個桶流入另一個桶,這樣就慢慢的實現了大檔案的複製過程。
Stream在nodejs中是EventEmitter的實現,並且有多種實現形式,例如:
(1).http responses request
(2).fs read write streams
(3).zlib streams
(4).tcp sockets
(5).child process stdout and stderr
上面的檔案複製可以簡單實現一下:
[JavaScript] 純文字檢視 複製程式碼var fs = require('fs'); var readStream = fs.createReadStream('/path/to/source'); var writeStream = fs.createWriteStream('/path/to/dest'); readStream.on('data', function(chunk) { // 當有資料流出時,寫入資料 writeStream.write(chunk); }); readStream.on('end', function() { // 當沒有資料時,關閉資料流 writeStream.end(); });
上面的寫法有一些問題,如果寫入的速度跟不上讀取的速度,有可能導致資料丟失。正常的情況應該是,寫完一段,再讀取下一段,如果沒有寫完的話,就讓讀取流先暫停,等寫完再繼續,於是程式碼可以修改為:
[JavaScript] 純文字檢視 複製程式碼var fs = require('fs'); var readStream = fs.createReadStream('/path/to/source'); var writeStream = fs.createWriteStream('/path/to/dest'); readStream.on('data', function(chunk) { // 當有資料流出時,寫入資料 if (writeStream.write(chunk) === false) { // 如果沒有寫完,暫停讀取流 readStream.pause(); } }); writeStream.on('drain', function() { // 寫完後,繼續讀取 readStream.resume(); }); readStream.on('end', function() { // 當沒有資料時,關閉資料流 writeStream.end(); });
或者使用更直接的pipe
[JavaScript] 純文字檢視 複製程式碼// pipe自動呼叫了data,end等事件 fs.createReadStream('/path/to/source').pipe(fs.createWriteStream('/path/to/dest'));
下面是一個更加完整的複製檔案的過程
[JavaScript] 純文字檢視 複製程式碼var fs = require('fs'), path = require('path'), out = process.stdout; var filePath = '/Users/chen/Movies/Game.of.Thrones.S04E07.1080p.HDTV.x264-BATV.mkv'; var readStream = fs.createReadStream(filePath); var writeStream = fs.createWriteStream('file.mkv'); var stat = fs.statSync(filePath); var totalSize = stat.size; var passedLength = 0; var lastSize = 0; var startTime = Date.now(); readStream.on('data', function(chunk) { passedLength += chunk.length; if (writeStream.write(chunk) === false) { readStream.pause(); } }); readStream.on('end', function() { writeStream.end(); }); writeStream.on('drain', function() { readStream.resume(); }); setTimeout(function show() { var percent = Math.ceil((passedLength / totalSize) * 100); var size = Math.ceil(passedLength / 1000000); var diff = size - lastSize; lastSize = size; out.clearLine(); out.cursorTo(0); out.write('已完成' + size + 'MB, ' + percent + '%, 速度:' + diff * 2 + 'MB/s'); if (passedLength < totalSize) { setTimeout(show, 500); } else { var endTime = Date.now(); console.log(); console.log('共用時:' + (endTime - startTime) / 1000 + '秒。'); } }, 500);
可以把上面的程式碼儲存為copy.js試驗一下
我們新增了一個遞迴的setTimeout(或者直接使用setInterval)來做一個旁觀者,每500ms觀察一次完成進度,並把已完成的大小、百分比和複製速度一併寫到控制檯上,當複製完成時,計算總的耗費時間,效果如圖:
複製了一集1080p的權利的遊戲第四季第7集,大概3.78G大小,由於使用了SSD,可以看到速度還是非常不錯的
複製完成後,顯示總花費時間
結合nodejs的readline, process.argv等模組,我們可以新增覆蓋提示、強制覆蓋、動態指定檔案路徑等完整的複製方法,有興趣的可以實現一下,實現完成,可以
[JavaScript] 純文字檢視 複製程式碼ln -s /path/to/copy.js /usr/local/bin/mycopy
這樣就可以使用自己寫的mycopy命令替代系統的cp命令
相關文章
- 深入理解nodejs Stream模組NodeJS
- node.js中流(Stream)的深度剖析Node.js
- nodejs的stream模組NodeJS
- Nodejs中的stream模組NodeJS
- nodeJS之流streamNodeJS
- 關於node.js中流的理解Node.js
- 淺析nodejs中的stream(流)NodeJS
- 瞭解nodeJs中的流(stream)NodeJS
- Nodejs教程24:Stream流NodeJS
- Nodejs 實踐 -- Stream 流NodeJS
- nodeJS基礎 Stream用法NodeJS
- 理解nodejs的moduleNodeJS
- nodejs中stream(流)學習分享NodeJS
- 理解nodejs模組的scopeNodeJS
- NodeJS stream 流 原理分析(附原始碼)NodeJS原始碼
- NodeJS 說說“重寫” 自定義stream 的實現NodeJS
- nodeJs檔案系統(fs)與流(stream)NodeJS
- Java中stream流的filter機制理解JavaFilter
- 淺度理解NodeJS的HTTP模組NodeJSHTTP
- 深入理解 Redis 新特性:StreamRedis
- Java學習之流Stream理解(一)Java
- 丐版stream流理解和使用
- NodeJs Stream的整理總結 (二) --雙工流Duplex和TransformNodeJSORM
- NodeJS Stream(可讀流、可寫流) API解讀NodeJSAPI
- NodeJs Stream的整理總結 (一) --可讀流與可寫流NodeJS
- 我是如何理解Java8 StreamJava
- 深入理解nodejs的HTTP處理流程NodeJSHTTP
- scala學習筆記:理解stream和view筆記View
- 深度理解Nodejs中crypto模組的安全知識NodeJS
- 理解TCP/IP、UDP - 通過nodejs的net模組TCPUDPNodeJS
- Java 8 Stream API: 深入理解與高效使用JavaAPI
- 深入理解nodejs中的非同步程式設計NodeJS非同步程式設計
- java專案中流式表示式的使用Java
- [譯] 理解 NodeJS 中基於事件驅動的架構NodeJS事件架構
- SQL Server中流水號生成的注意事項SQLServer
- Node.js 中的一股清流:理解 Stream(流)的基本概念Node.js
- Node.js 中流操作實踐Node.js
- [NodeJs系列]Q&A之理解NodeJs中的Event Loop、Timers以及process.nextTick()NodeJSOOP