国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

認識node核心模塊--從Buffer、Stream到fs

TANKING / 2160人閱讀

摘要:端輸入數據到端,對就是輸入流,得到的對象就是可讀流對就是輸出端得到的對象是可寫流。在中,這四種流都是的實例,它們都有事件,可讀流具有監聽數據到來的事件等,可寫流則具有監聽數據已傳給低層系統的事件等,和都同時實現了和的事件和接口。

原文地址在我的博客

node中的Buffer和Stream會給剛接觸Node的前端工程師們帶來困惑,原因是前端并沒有類似概念(or 有我們也沒意識到)。然而,在后端,在node中,Buffer和Stream處處體現。Buffer是緩沖區的意思,Stream是流的意思。在計算機中,緩沖區是存儲中間變量,方便CPU讀取數據的一塊存儲區域;流是類比水流形容數據的流動。Buffer和Stream一般都是字節級操作。本文將介紹這兩個模塊的具體細節后再介紹文件模塊,以讓讀者有更清晰的認識。

正文 二進制緩沖區Buffer

在前端,我們只需做字符串級別的操作,很少接觸字節、進制等底層操作,一方面這足以滿足日常需求,另一方面Javascript這種應用層語言并不是干這個的;然而在后端,處理文件、網絡協議、圖片、視頻等時是非常常見的,尤其像文件、網絡流等操作處理的都是二進制數據。為了讓javascript能夠處理二進制數據,node封裝了一個Buffer類,主要用于操作字節,處理二進制數據。

// 創建一個長度為 10、且用 30 填充的 Buffer。
const buf1 = Buffer.alloc(10, 30)
console.log(buf1)// 
// 字符串轉Buffer
const buf2 = Buffer.from("javascript")
console.log(buf2)// 
// 字符串轉 buffer
console.log(buf2.toString())// javascript
console.log(buf2.toString("hex")) //6a617661736372697074

一個 Buffer 類似于一個整數數組,可以取下標,有length屬性,有剪切復制操作等,很多API也類似數組,但Buffer的大小在被創建時確定,且無法調整。Buffer處理的是字節,兩位十六進制,因此在整數范圍就是0~255。

可以看到,Buffer可以與string互相轉化,還可以設置字符集編碼。Buffer用來處理文件I/O、網絡I/O傳輸的二進制數據,string用來呈現。在處理文件I/O、網絡I/O傳輸的二進制數據時,應該盡量以Buffer形式直接傳輸,速度會得到很好的提升,但操作字符串比操作Buffer還是快很多的。

Buffer內存分配與性能優化

Buffer是一個典型的javascript與C++結合的模塊,與性能有關的用C++來實現,javascript 負責銜接和提供接口。Buffer所占的內存不是V8分配的,是獨立于V8堆內存之外的內存,通過C++層面實現內存申請、javascript 分配內存。值得一提的是,每當我們使用Buffer.alloc(size)請求一個Buffer內存時,Buffer會以8KB為界限來判斷分配的是大對象還是小對象,小對象存入剩余內存池,不夠再申請一個8KB的內存池;大對象直接采用C++層面申請的內存。因此,對于一個大尺寸對象,申請一個大內存比申請眾多小內存池快很多。

流Stream

前面講到,流類比水流形容數據的流動,在文件I/O、網絡I/O中數據的傳輸都可以稱之為流,流是能統一描述所有常見輸入輸出類型的模型,是順序讀寫字節序列的抽象表示。數據從A端流向B端與從B端流向A端是不一樣的,因此,流是有方向的。A端輸入數據到B端,對B就是輸入流,得到的對象就是可讀流;對A就是輸出端、得到的對象是可寫流。有的流即可以讀又可以寫,如TCP連接,Socket連接等,稱為讀寫流(Duplex)。還有一種在讀寫過程中可以修改和變換數據的讀寫流稱為Transform流。

在node中,這些流中的數據就是Buffer對象,可讀、可寫流會將數據存儲到內部的緩存中,等待被消費;Duplex 和 Transform 則是都維護了兩個相互獨立的緩存用于讀和寫。 在維持了合理高效的數據流的同時,也使得對于讀和寫可以獨立進行而互不影響。

在node中,這四種流都是EventEmitter的實例,它們都有close、error事件,可讀流具有監聽數據到來的data事件等,可寫流則具有監聽數據已傳給低層系統的finish事件等,Duplex 和 Transform 都同時實現了 Readable 和 Writable 的事件和接口 。

值得一提的是writable的drain事件,這個事件表示緩存的數據被排空了。為什么有這個事件呢?起因是調用可寫流的write和可讀流的read都會有一個緩存區用來緩存寫/讀的數據,緩存區是有大小的,一旦寫的內容超過這個大小,write方法就會返回false,表示寫入停止,這時如果繼續read完緩存區數據,緩存區被排空,就會觸發drain事件,可以這樣來防止緩存區爆倉:

var rs = fs.createReadStream(src);
var ws = fs.createWriteStream(dst);

rs.on("data", function (chunk) {
    if (ws.write(chunk) === false) {
        rs.pause();
    }
});

rs.on("end", function () {
    ws.end();
});

ws.on("drain", function () {
    rs.resume();
});

一些常見流分類:

可寫流:HTTP requests, on the client、HTTP responses, on the server、fs write streams、zlib streams、crypto streams、TCP sockets、child process stdin、process.stdout, process.stderr

可讀流:HTTP responses, on the client、HTTP requests, on the server、fs read streams、zlib streams、crypto streams、TCP sockets、child process stdout and stderr、process.stdin

可讀可寫流:TCP sockets、zlib streams、crypto streams

變換流:zlib streams、crypto streams

另外,提到流就不得不提到管道的概念,這個概念也非常形象:水流從一端到另一端流動需要管道作為通道或媒介。流也是這樣,數據在端之間的傳送也需要管道,在node中是這樣的:

// 將 readable 中的所有數據通過管道傳遞給名為 file.txt 的文件
const readable = getReadableStreamSomehow();
const writable = getWritableStreamSomehow("file.txt");
// readable 中的所有數據都傳給了 "file.txt"
readable.pipe(writable);

// 對流進行鏈式地管道操作
const r = fs.createReadStream("file.txt");
const z = zlib.createGzip();
const w = fs.createWriteStream("file.txt.gz");
r.pipe(z).pipe(w);

注意,只有可讀流才具有pipe能力,可寫流作為目的地。

pipe不僅可以作為通道,還能很好的控制管道里的流,控制讀和寫的平衡,不讓任一方過度操作。另外,pipe可以監聽可讀流的data、end事件,這樣就可以構建快速的響應:

// 一個文件下載的例子,使用回調函數的話需要等到服務器讀取完文件才能向瀏覽器發送數據
var http = require("http") ;
var fs = require("fs") ;
var server = http.createServer(function (req, res) {
    fs.readFile(__dirname + "/data.txt", function (err, data) {
        res.end(data);
    }) ;
}) ;
server.listen(8888) ;

// 而采用流的方式,只要建立連接,就會接受到數據,不用等到服務器緩存完data.txt
var http = require("http") 
var fs = require("fs") 
var server = http.createServer(function (req, res) {
    var stream = fs.createReadStream(__dirname + "/data.txt") 
    stream.pipe(res) 
}) 
server.listen(8888) 

因此,使用pipe即可解決上面那個爆倉問題。

fs文件模塊

fs文件模塊是高階模塊,繼承了EventEmitter、stream、path等底層模塊,提供了對文件的操作,包括文件的讀取、寫入、更名、刪除、遍歷目錄、鏈接POSIX文件系統等操作。與node設計思想和其他模塊不同的是,fs模塊中的所有操作都提供了異步和同步兩個版本。fs模塊主要由下面幾部分組成:

對底層POSIX文件系統的封裝,對應于操作系統的原生文件操作

繼承Stream的文件流 fs.createReadStream和fs.createWriteStream

同步文件操作方法,如fs.readFileSync、fs.writeFileSync

異步文件操作方法, fs.readFile和fs.writeFile

模塊API架構如下:

讀寫操作:

const fs = require("fs"); // 引入fs模塊
/* 讀文件 */

// 使用流
const read = fs.createReadStream("sam.js",{encoding:"utf8"});
read.on("data",(str)=>{
    console.log(str);
})
// 使用readFile
fs.readFile("test.txt", {}, function(err, data) {
    if (err) {
        throw err;
    }
    console.log(data);
});
// open + read
fs.open("test.txt","r",(err, fd) => {
    fs.fstat(fd,(err,stat)=>{
        var len = stat.size;  //檢測文件長度
        var buf = new Buffer(len);
        fs.read(fd,buf,0,len,0,(err,bw,buf)=>{
            console.log(buf.toString("utf8"));
            fs.close(fd);
        })
    });
});

/* 寫文件與讀取文件API形式類似 */

讀/寫文件都有三種方式,那么區別是什么呢?

createReadStream/createWriteStream創建一個將文件內容讀取為流數據的ReadStream對象,這個方法主要目的就是把數據讀入到流中,得到是可讀流,方便以流進行操作

readFile/writeFile:Node.js會將文件內容視為一個整體,為其分配緩存區并且一次性將文件內容讀/寫取到緩存區中,在這個期間,Node.js將不能執行任何其他處理,所以當讀寫大文件的時候,有可能造成緩存區“爆倉”

read/write讀/寫文件內容是不斷地將文件中的一小塊內容讀/寫入緩存區,最后從該緩存區中讀取文件內容

同步API也是如此。其中最常用的是readFile,讀取大文件則采取用,read則提供更為細節、底層的操作,而且read要配合open。

獲取文件的狀態:

fs.stat("eda.txt", (err, stat) => {
  if (err)
    throw err
  console.log(stat)
})
/* 
Stats {
  dev: 16777220,
  mode: 33279,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4194304,
  ino: 4298136825,
  size: 0,
  blocks: 0,
  atimeMs: 1510317983760.94, - 文件數據最近被訪問的時間
  mtimeMs: 1510317983760.94, - 文件數據最近被修改的時間。
  ctimeMs: 1510317983777.8538, - 文件狀態最近更改的時間
  birthtimeMs: 1509537398000,
  atime: 2017-11-10T12:46:23.761Z,
  mtime: 2017-11-10T12:46:23.761Z,
  ctime: 2017-11-10T12:46:23.778Z,
  birthtime: 2017-11-01T11:56:38.000Z 
}*/

監聽文件:

const FSWatcher = fs.watch("eda.txt", (eventType, filename) => {
    console.log(`${eventType}`)
})
FSWatcher.on("change", (eventType, filename) => {
    console.log(`${filename}`)
})
// watch和返回的FSWatcher實例的回調函數都綁定在了 change 事件上

fs.watchFile("message.text", (curr, prev) => {
  console.log(`the current mtime is: ${curr.mtime}`);
  console.log(`the previous mtime was: ${prev.mtime}`);
})

監聽文件仍然有兩種方法:

watch 調用的是底層的API來監視文件,很快,可靠性也較高

watchFile 是通過不斷輪詢 fs.Stat (文件的統計數據)來獲取被監視文件的變化,較慢,可靠性較低,另外回調函數的參數是 fs.Stat 實例

因此盡可能多的使用watch,watchFile 用于需要得到文件更多信息的場景。

其他

創建、刪除、復制、移動、重命名、檢查文件、修改權限...

總結

由Buffer到Stream,再到fs文件模塊,將它們串聯起來能對整塊知識有更清晰的認識,也對webpack、gulp等前端自動化工具構建工作流的機制和實現有了更深的了解。學習其他知識亦是如此——知道來龍去脈,知道為什么會存在,知道它們之間的聯系,就能讓碎片化的知識串聯起來,能讓它們make sense,能夠讓自己“上的廳堂、下得廚房”。

參考:

nodeJs高階模塊--fs

deep into node

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89707.html

相關文章

  • Node.js設計模式》使用流進行編碼

    摘要:如何創建并使用。正如我們所預料到的那樣,使用來進行大文件的讀取顯然是錯誤的。使用進行壓縮文件我們必須修復我們的應用程序,并使其處理大文件的最簡單方法是使用的。確切地說,由返回的流。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關注我的專欄,之后的博文將在專欄同步: En...

    xinhaip 評論0 收藏0
  • 閱讀gulp源碼小結

    摘要:源碼簡介源碼核心部分寥寥行。同時本身是直接繼承于模塊。寫在末尾閱讀代碼的這一次,是我第一次閱讀這種開源的模塊化項目。深深的被震撼到了,認識到了模塊化的巨大力量。就能完成非常復雜的事情,而不需要凡是親力親為,一行行代碼,一個個小問題依次解決。 gulp源碼簡介 gulp源碼核心部分寥寥60+行。但是通過這60+行代碼,gulp給我們帶來的確是前端自動化構建的便利。以往以為其源碼肯定蠻復雜...

    Rocture 評論0 收藏0
  • Node.js 中度體驗

    摘要:創建簡單應用使用指令來載入模塊創建服務器使用方法創建服務器,并使用方法綁定端口。全局安裝將安裝包放在下。的核心就是事件觸發與事件監聽器功能的封裝。通常我們用于從一個流中獲取數據并將數據傳遞到另外一個流中。壓縮文件為文件壓縮完成。 創建簡單應用 使用 require 指令來載入 http 模塊 var http = require(http); 創建服務器 使用 http.create...

    CastlePeaK 評論0 收藏0
  • Node.js學習總結

    摘要:表示當前正在執行的腳本的文件名。默認編碼為模式為,為回調函數,回調函數只包含錯誤信息參數,在寫入失敗時返回。參數使用說明如下通過方法返回的文件描述符。 Node.js回調 Node.js異步編程的直接體現就是回調。 阻塞代碼: const fs = require(fs); let data = fs.readFileSync(input.txt); console.log(data...

    kamushin233 評論0 收藏0
  • node.js入門學習筆記整理——基礎篇

    摘要:的介紹一般是這樣在中,類是隨內核一起發布的核心庫。庫為帶來了一種存儲原始數據的方法,可以讓處理二進制數據,每當需要在中處理操作中移動的數據時,就有可能使用庫。這樣傳遞數據會更快。 零、開始之前 1、 首先解釋一下node.js是什么? 2、node.js和javascript有什么不同? 1)因為javascript主要是用在browser,而node.js是在server或者你的電腦...

    Tamic 評論0 收藏0

發表評論

0條評論

TANKING

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<