成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

認(rèn)識node核心模塊--從Buffer、Stream到fs

TANKING / 2204人閱讀

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

原文地址在我的博客

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

正文 二進(jìn)制緩沖區(qū)Buffer

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

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

一個 Buffer 類似于一個整數(shù)數(shù)組,可以取下標(biāo),有l(wèi)ength屬性,有剪切復(fù)制操作等,很多API也類似數(shù)組,但Buffer的大小在被創(chuàng)建時確定,且無法調(diào)整。Buffer處理的是字節(jié),兩位十六進(jìn)制,因此在整數(shù)范圍就是0~255。

可以看到,Buffer可以與string互相轉(zhuǎn)化,還可以設(shè)置字符集編碼。Buffer用來處理文件I/O、網(wǎng)絡(luò)I/O傳輸?shù)亩M(jìn)制數(shù)據(jù),string用來呈現(xiàn)。在處理文件I/O、網(wǎng)絡(luò)I/O傳輸?shù)亩M(jìn)制數(shù)據(jù)時,應(yīng)該盡量以Buffer形式直接傳輸,速度會得到很好的提升,但操作字符串比操作Buffer還是快很多的。

Buffer內(nèi)存分配與性能優(yōu)化

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

流Stream

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

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

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

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

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

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

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

// 對流進(jìn)行鏈?zhǔn)降毓艿啦僮?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可以監(jiān)聽可讀流的data、end事件,這樣就可以構(gòu)建快速的響應(yīng):

// 一個文件下載的例子,使用回調(diào)函數(shù)的話需要等到服務(wù)器讀取完文件才能向瀏覽器發(fā)送數(shù)據(jù)
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) ;

// 而采用流的方式,只要建立連接,就會接受到數(shù)據(jù),不用等到服務(wù)器緩存完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文件系統(tǒng)等操作。與node設(shè)計思想和其他模塊不同的是,fs模塊中的所有操作都提供了異步和同步兩個版本。fs模塊主要由下面幾部分組成:

對底層POSIX文件系統(tǒng)的封裝,對應(yīng)于操作系統(tǒng)的原生文件操作

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

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

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

模塊API架構(gòu)如下:

讀寫操作:

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形式類似 */

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

createReadStream/createWriteStream創(chuàng)建一個將文件內(nèi)容讀取為流數(shù)據(jù)的ReadStream對象,這個方法主要目的就是把數(shù)據(jù)讀入到流中,得到是可讀流,方便以流進(jìn)行操作

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

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

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

獲取文件的狀態(tài):

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, - 文件數(shù)據(jù)最近被訪問的時間
  mtimeMs: 1510317983760.94, - 文件數(shù)據(jù)最近被修改的時間。
  ctimeMs: 1510317983777.8538, - 文件狀態(tài)最近更改的時間
  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 
}*/

監(jiān)聽文件:

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

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

監(jiān)聽文件仍然有兩種方法:

watch 調(diào)用的是底層的API來監(jiān)視文件,很快,可靠性也較高

watchFile 是通過不斷輪詢 fs.Stat (文件的統(tǒng)計數(shù)據(jù))來獲取被監(jiān)視文件的變化,較慢,可靠性較低,另外回調(diào)函數(shù)的參數(shù)是 fs.Stat 實例

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

其他

創(chuàng)建、刪除、復(fù)制、移動、重命名、檢查文件、修改權(quán)限...

總結(jié)

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

參考:

nodeJs高階模塊--fs

deep into node

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/89707.html

相關(guān)文章

  • Node.js設(shè)計模式》使用流進(jìn)行編碼

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

    xinhaip 評論0 收藏0
  • 閱讀gulp源碼小結(jié)

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

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

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

    CastlePeaK 評論0 收藏0
  • Node.js學(xué)習(xí)總結(jié)

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

    kamushin233 評論0 收藏0
  • node.js入門學(xué)習(xí)筆記整理——基礎(chǔ)篇

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

    Tamic 評論0 收藏0

發(fā)表評論

0條評論

TANKING

|高級講師

TA的文章

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