Node.js Streams:你須要知道的一切

Node.js Streams:你須要知道的一切

圖像來源html

Node.js流以難以使用而聞名,甚至更難理解。好吧,我有個好消息 - 再也不是這樣了。node

多年來,開發人員在那裏建立了許多軟件包,其惟一目的是簡化流程。但在本文中,我將重點介紹本機Node.js流APIlinux

「Streams是Node最好,也是最容易被誤解的想法。」
- Dominic Tarr

什麼是溪流?

流是數據的集合 - 就像數組或字符串同樣。不一樣之處在於流可能沒法一次所有可用,而且它們沒必要適合內存。這使得流真正強大的大量數據,或者數據這是一個從外部來源有人來工做時,大塊的時間。api

可是,流不只僅是處理大數據。它們還爲咱們提供了代碼中可組合性的強大功能。就像咱們能夠經過管道其餘較小的Linux命令來組成強大的linux命令同樣,咱們能夠在Node中使用流徹底相同。數組

與Linux命令的可組合性服務器

const wc = ... //用於wc輸入的流const grep = ... //用於grep輸出的流
const wc = ... //用於wc輸入的流
grep.pipe(WC)

Node中的許多內置模塊實現了流接口:異步

從個人Pluralsight課程 - Advanced Node.js中捕獲的截圖ide

上面的列表提供了一些本機Node.js對象的示例,這些對象也是可讀寫的流。其中一些對象是可讀寫的流,如TCP套接字,zlib和加密流。函數

請注意,對象也是密切相關的。雖然HTTP響應是客戶端上的可讀流,但它是服務器上的可寫流。這是由於在HTTP狀況下,咱們基本上從一個對象(http.IncomingMessage)讀取並寫入另外一個(http.ServerResponse)。學習

另外要注意的是如何stdio流(stdinstdoutstderr)有反流類型,當涉及到的子進程。這容許一種很是簡單的方法來管理來自主流程stdio流的這些流。

一個實際的例子

理論很棒,但每每不是100%使人信服。讓咱們看一個示例,演示在內存消耗方面流能夠在代碼中產生的差別。

讓咱們先建立一個大文件:

const file = fs.createWriteStream('./ big.file'); 

for(let i = 0; i <= 1e6; i ++){ 
  file.write('Lorem ipsum dolor sit amet,consectetur adipisicing elit,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam,quis nostrud練習ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in repreptderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur。Excepteur sint occaecat cupidatat non proident,sunt in culpa qui officia deserunt mollit anim id est laborum。\ n' ); 
} 

file.end();const fs = require('fs'); 
const file = fs.createWriteStream('./ big.file'); 

for(let i = 0; i <= 1e6; i ++){ 
  file.write('Lorem ipsum dolor sit amet,consectetur adipisicing elit,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam,quis nostrud練習ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in repreptderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur。Excepteur sint occaecat cupidatat non proident,sunt in culpa qui officia deserunt mollit anim id est laborum。\ n' ); 
} 

file.end();

看看我用來建立那個大文件的東西。一個可寫的流!

fs模塊可用於使用流接口讀取和寫入文件。在上面的例子中,咱們big.file經過帶有循環的可寫流100萬行來寫入。

運行上面的腳本會生成大約約400 MB的文件。

這是一個簡單的Node Web服務器,專門用於big.file

const server = require('http')。createServer(); 

server.on('request',(req,res)=> { 
  fs.readFile('./ big.file',(err,data)=> { 
    if(err)throw err; 
  
    res.end(data); 
  }); 
}); 

server.listen(8000);const fs = require('fs'); 
const server = require('http')。createServer(); 

server.on('request',(req,res)=> { 
  fs.readFile('./ big.file',(err,data)=> { 
    if(err)throw err; 
  
    res.end(data); 
  }); 
}); 

server.listen(8000);

當服務器收到請求時,它將使用異步方法爲大文件提供服務fs.readFile。可是,嘿,這不像咱們阻止事件循環或任何事情。每件事都很棒,對嗎?對?

好吧,讓咱們看看當咱們運行服務器,鏈接到它並監視內存時會發生什麼。

當我運行服務器時,它開始時具備正常的內存量,8.7 MB:

而後我鏈接到服務器。注意消耗的內存發生了什麼:

哇 - 內存消耗躍升至434.8 MB。

在將big.file它們寫入響應對象以前,咱們基本上將整個內容放在內存中。這是很是低效的。

HTTP響應對象(res在上面的代碼中)也是可寫流。這意味着若是咱們有一個表示內容的可讀流big.file,咱們能夠將這兩個相互管道並實現大體相同的結果,而不會消耗~400 MB的內存。

Node的fs模塊能夠爲使用該createReadStream方法的任何文件提供可讀流。咱們能夠將它傳遞給響應對象:

const server = require('http')。createServer(); 

server.on('request',(req,res)=> { 
  const src = fs.createReadStream('./ big.file'); 
  src.pipe(res);
 }); 

server.listen(8000);const fs = require('fs'); 
const server = require('http')。createServer(); 

server.on('request',(req,res)=> { 
  const src = fs.createReadStream('./ big.file'); 
  src.pipe(res);
 }); 

server.listen(8000);

如今當你鏈接到這個服務器時,會發生一件神奇的事情(看一下內存消耗):

發生了什麼?

當客戶端請求該大文件時,咱們一次流一個塊,這意味着咱們根本不在內存中緩衝它。內存使用量增加了大約25 MB,就是這樣。

您能夠將此示例推到極限。big.file使用500萬行而不是僅僅100萬行從新生成,這將使文件超過2 GB,而且實際上大於Node中的默認緩衝區限制。

若是您嘗試使用該文件fs.readFile,默認狀況下您根本不能(您能夠更改限制)。可是fs.createReadStream,對於請求者來講,將2 GB的數據流傳輸到請求者是沒有問題的,最重要的是,進程內存使用量大體相同。

準備好學習流了嗎?

本文是 關於Node.js的Pluralsight課程的一部分。我在那裏報道了相似的視頻格式內容。

流101

Node.js中有四種基本流類型:可讀,可寫,雙工和變換流。

  • 可讀流是能夠從中消費數據的源的抽象。一個例子是fs.createReadStream方法。
  • 可寫流是能夠寫入數據的目標的抽象。一個例子是fs.createWriteStream方法。
  • 雙工流是可讀和可寫的。一個例子是TCP套接字。
  • 變換流基本上是雙工流,可用於在寫入和讀取數據時修改或轉換數據。一個例子是zlib.createGzip使用gzip壓縮數據的流。您能夠將轉換流視爲一個函數,其中輸入是可寫流部分,輸出是可讀流部分。您可能還會聽到稱爲「 直通流 」的轉換

全部流都是EventEmitter。它們發出可用於讀取和寫入數據的事件。可是,咱們可使用該pipe方法以更簡單的方式使用流數據。

管道方法

這是你須要記住的神奇線條:

 .pipe(writableDestreadableSrc .pipe(writableDest

在這個簡單的行中,咱們管道輸出可讀流 - 數據源,做爲可寫流的輸入 - 目的地。源必須是可讀流,目標必須是可寫的。固然,它們也能夠是雙工/變換流。事實上,若是咱們正在進入雙工流,咱們能夠像在Linux中同樣連接管道調用:

  .pipe(transformStream1)
  .pipe(transformStream2)
  .pipe(finalWrtitableDest)readableSrc 
  .pipe(transformStream1)
  .pipe(transformStream2)
  .pipe(finalWrtitableDest)

pipe方法返回目標流,這使咱們可以進行上述連接。對於流a(讀取),bc(雙面),和d(可寫的),咱們能夠:

a.pipe(b)中.pipe(c)中.pipe(d)
a.pipe(b)
b.pipe(c)
c.pipe(d)#這至關於:
a.pipe(b)
b.pipe(c)
c.pipe(d)
$ a | b | c | d#在Linux中,至關於:
$ a | b | c | d

pipe方法是消費流的最簡單方法。一般建議使用該pipe方法或使用事件消耗流,但避免混合這二者。一般,當您使用該pipe方法時,您不須要使用事件,但若是您須要以更自定義的方式使用流,則事件將是可行的方法。

流事件

除了從可讀流源讀取並寫入可寫目的地以外,該pipe方法還會自動管理一些事情。例如,它處理錯誤,文件結束以及一個流比另外一個流更慢或更快的狀況。

可是,流也能夠直接與事件一塊兒使用。這是該pipe方法主要用於讀寫數據的簡化事件等效代碼:

#readed.pipe(可寫)
  writable.write(chunk); 
});readable.on('data',(chunk)=> { 
  writable.write(chunk); 
});
  writable.end(); 
});readable.on('end',()=> { 
  writable.end(); 
});

如下是可與可讀寫流一塊兒使用的重要事件和函數的列表:

從個人Pluralsight課程 - Advanced Node.js中捕獲的截圖

事件和函數以某種方式相關,由於它們一般一塊兒使用。

可讀流上最重要的事件是:

  • data每當流將一大塊數據傳遞給使用者時發出的事件
  • end事件,在沒有更多數據要從流中消耗時發出。

可寫流上最重要的事件是:

  • drain事件,是可寫流能夠接收更多數據的信號。
  • finish事件,在將全部數據刷新到基礎系統時發出。

能夠組合事件和功能,以實現流的自定義和優化使用。要使用可讀流,咱們可使用pipeunpipemethods或readunshiftresume方法。要使用可寫流,咱們能夠將它做爲pipe/ 的目標unpipe,或者只是使用write方法寫入它,並end在完成後調用方法。

可讀流的暫停和流動模式

可讀流有兩種主要模式影響咱們使用它們的方式:

  • 它們能夠處於暫停模式
  • 或者在流動模式下

這些模式有時被稱爲拉動和推進模式。

默認狀況下,全部可讀流都以暫停模式啓動,但它們能夠輕鬆切換爲流動,並在須要時返回暫停狀態。有時,切換會自動進行。

當可讀流處於暫停模式時,咱們可使用該read()方法根據須要從流中讀取,可是,對於流動模式中的可讀流,數據不斷流動,咱們必須監聽事件以使用它。

在流動模式下,若是沒有消費者能夠處理數據,實際上可能會丟失數據。這就是爲何當咱們在流動模式下有可讀流時,咱們須要一個data事件處理程序。實際上,只需添加一個data事件處理程序就能夠將暫停的流切換爲流動模式,並刪除data事件處理程序會將流切換回暫停模式。其中一些是爲了向後兼容舊的Node流接口而完成的。

要在這兩種流模式之間手動切換,可使用resume()pause()方法。

從個人Pluralsight課程 - Advanced Node.js中捕獲的截圖

使用該pipe方法消耗可讀流時,咱們沒必要擔憂這些模式會pipe自動管理它們。

實現流

當咱們在Node.js中討論流時,有兩個主要的不一樣任務:

  • 實現流的任務。
  • 消費它們的任務。

到目前爲止,咱們一直在談論只消耗流。讓咱們實施一些!

流實現者一般requirestream模塊的人。

實現可寫流

要實現可寫流,咱們須要使用Writable流模塊中的構造函數。

const {Writable} = require('stream');

咱們能夠經過多種方式實現可寫流。例如,Writable若是須要,咱們能夠擴展構造函數

class myWritableStream擴展Writable { 
}
}

可是,我更喜歡更簡單的構造方法。咱們只是從Writable構造函數建立一個對象,並傳遞一些選項。惟一須要的選項是write暴露要寫入的數據塊的函數。

const {Writable} = require('stream');
const outStream = new Writable({ 
  write(chunk,encoding,callback){ 
    console.log(chunk.toString()); 
    callback(); 
  } 
}); 

process.stdin.pipe(outStream);
  write(chunk,encoding,callback){ 
    console.log(chunk.toString()); 
    callback(); 
  } 
}); 

process.stdin.pipe(outStream);

這個write方法有三個參數。

  • 一般是一個緩衝區,除非咱們配置不一樣的數據流。
  • 編碼參數,須要在這種狀況下,但一般咱們能夠忽略它。
  • 回調是咱們須要咱們完成處理數據塊以後調用一個函數。這就是寫入是否成功的信號。要發出故障信號,請使用錯誤對象調用回調。

outStream,咱們只是console.log將塊做爲字符串,並callback在沒有錯誤的狀況下調用以後表示成功。這是一個很是簡單且可能不那麼有用的回聲流。它將回應它收到的任何東西。

要使用這個流,咱們能夠簡單地使用它process.stdin,這是一個可讀的流,因此咱們能夠直接process.stdin進入咱們的流outStream

當咱們運行上面的代碼時,咱們輸入的任何內容都process.stdin將使用該outStream console.log行回顯。

這不是一個很是有用的實現流,由於它實際上已經實現和內置。這很是至關於process.stdout。咱們能夠直接stdin進入stdout,咱們將經過這一行得到徹底相同的回聲功能:

process.stdin.pipe(process.stdout);

實現可讀流

要實現可讀流,咱們須要Readable接口,並從中構造一個對象,並read()在流的配置參數中實現一個方法:

const {Readable} = require('stream');
const inStream = new Readable({ 
  read(){} 
});
  read(){} 
});

有一種實現可讀流的簡單方法。咱們能夠直接push使用咱們但願消費者使用的數據。

const {Readable} = require('stream');
const inStream = new Readable({ 
  read(){} 
});
  read(){} 
});
inStream中。('ABCDEFGHIJKLM'); 
inStream中。('NOPQRSTUVWXYZ');
('ABCDEFGHIJKLM'); 
inStream中。('NOPQRSTUVWXYZ');
inStream中。(null); //沒有更多數據
(null); //沒有更多數據
inStream.pipe(process.stdout);

當咱們push成爲一個null對象時,這意味着咱們想要發信號通知該流沒有更多數據。

要使用這個簡單的可讀流,咱們能夠簡單地將其傳輸到可寫流中process.stdout

當咱們運行上面的代碼時,咱們將從中讀取全部數據inStream並將其回顯到標準輸出。很簡單,但也不是頗有效率。

咱們基本上推流中的全部數據以前,它管道到process.stdout。當消費者要求時,更好的方法是按需推送數據。咱們能夠經過read()在配置對象中實現該方法來實現:

const inStream = new Readable({ 
  read(size){ 
    //對數據有需求......有人想讀它。
  } 
});
  read(size){ 
    //對數據有需求......有人想讀它。
  } 
});

當在可讀流上調用read方法時,實現能夠將部分數據推送到隊列。例如,咱們能夠一次推送一個字母,從字符代碼65(表明A)開始,並在每次推送時遞增:

const inStream = new Readable({ 
  read(size){ 
    this.push(String.fromCharCode(this.currentCharCode ++)); 
    if(this.currentCharCode> 90){ 
      this.push(null); 
    } 
  } 
});
  read(size){ 
    this.push(String.fromCharCode(this.currentCharCode ++)); 
    if(this.currentCharCode> 90){ 
      this.push(null); 
    } 
  } 
});
inStream.currentCharCode = 65;
inStream.pipe(process.stdout);

當消費者正在閱讀可讀流時,該read方法將繼續觸發,而且咱們將推送更多字母。咱們須要在某個地方中止這個循環,這就是當currentCharCode大於90(表示Z)時if語句推送null的緣由。

這段代碼至關於咱們開始使用的更簡單的代碼,但如今咱們在消費者要求時按需推送數據。你應該老是那樣作。

實現雙工/轉換流

使用Duplex流,咱們可使用相同的對象實現可讀和可寫流。就像咱們從兩個接口繼承同樣。

這是一個示例雙工流,它結合了上面實現的兩個可寫和可讀示例:

const {Duplex} = require('stream'); 

const inoutStream = new Duplex({ 
  write(chunk,encoding,callback){ 
    console.log(chunk.toString()); 
    callback(); 
  },

  read(size){ 
    this.push(String.fromCharCode(this.currentCharCode ++) ); 
    if(this.currentCharCode> 90){ 
      this.push(null); 
    } 
  } 
}); 

inoutStream.currentCharCode = 65;
const inoutStream = new Duplex({ 
  write(chunk,encoding,callback){ 
    console.log(chunk.toString()); 
    callback(); 
  },

  read(size){ 
    this.push(String.fromCharCode(this.currentCharCode ++) ); 
    if(this.currentCharCode> 90){ 
      this.push(null); 
    } 
  } 
}); 

inoutStream.currentCharCode = 65;
<strong>process.stdin.pipe(inoutStream).pipe(process.stdout);</strong>

經過組合這些方法,咱們可使用此雙工流來讀取A到Z中的字母,咱們也能夠將其用做其回聲功能。咱們將可讀stdin流傳輸到此雙工流中以使用echo功能,咱們將雙工流自己stdout傳輸到可寫流中以查看字母A到Z.

重要的是要理解雙工流的可讀和可寫側徹底獨立地操做。這僅僅是將兩個特徵分組到一個對象中。

變換流是更有趣的雙工流,由於其輸出是根據其輸入計算的。

對於轉換流,咱們沒必要實現readwrite方法,咱們只須要實現一個transform結合它們的方法。它具備write方法的簽名,咱們也能夠將它用於push數據。

這是一個簡單的變換流,在將其轉換爲大寫格式以後回顯您輸入的任何內容:

const {Transform} = require('stream'); 

const upperCaseTr = new Transform({ 
  transform(chunk,encoding,callback){ 
    this.push(chunk.toString()。toUpperCase()); 
    callback(); 
  } 
}); 

process.stdin.pipe(upperCaseTr).pipe(process.stdout);
const upperCaseTr = new Transform({ 
  transform(chunk,encoding,callback){ 
    this.push(chunk.toString()。toUpperCase()); 
    callback(); 
  } 
}); 

process.stdin.pipe(upperCaseTr).pipe(process.stdout);

在這個咱們正在消耗的變換流中,就像前面的雙工流示例同樣,咱們只實現了一個transform()方法。在該方法中,咱們將chunk其轉換爲大寫版本,而後push將該版本轉換爲可讀部分。

流對象模式

默認狀況下,流指望緩衝區/字符串值。objectMode咱們能夠設置一個標誌,讓流接受任何JavaScript對象。

這是一個簡單的例子來證實這一點。如下轉換流組合使得一個功能能夠將逗號分隔值的字符串映射到JavaScript對象中。所以「a,b,c,d」變得{a: b, c: d}

const {Transform} = require('stream');
const commaSplitter = new Transform({ 
  readableObjectMode:true,
  readableObjectMode:true,
transform(chunk,encoding,callback){ 
    this.push(chunk.toString()。trim()。split(',')); 
    打回來(); 
  } 
};
    this.push(chunk.toString()。trim()。split(',')); 
    打回來(); 
  } 
};
const arrayToObject = new Transform({ 
  readableObjectMode:true,
  writableObjectMode:true,
  readableObjectMode:true,
  writableObjectMode:true,
transform(chunk,encoding,callback){ 
    const obj = {}; 
    for(let i = 0; i <chunk.length; i + = 2){ 
      obj [chunk [i]] = chunk [i + 1]; 
    } 
    this.push(obj); 
    打回來(); 
  } 
};
    const obj = {}; 
    for(let i = 0; i <chunk.length; i + = 2){ 
      obj [chunk [i]] = chunk [i + 1]; 
    } 
    this.push(obj); 
    打回來(); 
  } 
};
const objectToString = new Transform({ 
  writableObjectMode:true,
  writableObjectMode:true,
transform(chunk,encoding,callback){ 
    this.push(JSON.stringify(chunk)+'\ n'); 
    打回來(); 
  } 
};
    this.push(JSON.stringify(chunk)+'\ n'); 
    打回來(); 
  } 
};
process.stdin 
  .pipe(commaSplitter)
  .pipe(arrayToObject)
  .pipe(
  objectToString ).pipe(process.stdout)
  .pipe(commaSplitter)
  .pipe(arrayToObject)
  .pipe(
  objectToString ).pipe(process.stdout)

咱們傳遞輸入字符串(例如「a,b,c,d」),經過commaSplitter該字符串將數組推送爲可讀數據([「a」, 「b」, 「c」, 「d」])。readableObjectMode在該流上添加標誌是必要的,由於咱們在那裏推送一個對象,而不是字符串。

而後咱們將數組並將其arrayToObject傳輸到流中。咱們須要一個writableObjectMode標誌來使該流接受一個對象。它還會推送一個對象(映射到對象的輸入數組),這就是爲何咱們也須要readableObjectMode那裏的標誌。最後一個objectToString流接受一個對象,但推出一個字符串,這就是爲何咱們只須要一個writableObjectMode標誌。可讀部分是普通字符串(字符串化對象)。

使用上面的例子

Node的內置轉換流

Node有一些很是有用的內置變換流。即,zlib和加密流。

這是一個使用zlib.createGzip()流與fs可讀/可寫流相結合來建立文件壓縮腳本的示例:

const zlib = require('zlib'); 
const file = process.argv [2]; 

fs.createReadStream(file)
  .pipe(zlib.createGzip())。
  pipe(fs.createWriteStream(file +'.gz'));const fs = require('fs'); 
const zlib = require('zlib'); 
const file = process.argv [2]; 

fs.createReadStream(file)
  .pipe(zlib.createGzip())。
  pipe(fs.createWriteStream(file +'.gz'));

您可使用此腳本將您傳遞的任何文件做爲參數進行gzip。咱們將該文件的可讀流傳輸到zlib內置轉換流中,而後傳輸到新gzip壓縮文件的可寫流中。簡單。

使用管道的一個很酷的事情是,若是須要,咱們實際上能夠將它們與事件結合起來。好比說,我但願用戶在腳本工做時看到進度指示器,在腳本完成時看到「完成」消息。因爲該pipe方法返回目標流,咱們也能夠連接事件處理程序的註冊:

const zlib = require('zlib'); 
const file = process.argv [2]; 

fs.createReadStream(file)
  .pipe(zlib.createGzip())。
  on('data',()=> process.stdout.write('。'))。
   pipe(fs.createWriteStream(file +'.zz' ))。
  on('finish',()=> console.log('完成'));const fs = require('fs'); 
const zlib = require('zlib'); 
const file = process.argv [2]; 

fs.createReadStream(file)
  .pipe(zlib.createGzip())。
 on('data',()=> process.stdout.write('。'))。
   pipe(fs.createWriteStream(file +'.zz' ))。
  on('finish',()=> console.log('完成'));

所以,經過該pipe方法,咱們能夠輕鬆地使用流,但咱們仍然可使用須要的事件進一步自定義與這些流的交互。

這個pipe方法的優勢在於咱們能夠用一種可讀的方式逐個編寫程序。例如,data咱們能夠簡單地建立一個轉換流來報告進度,而不是監聽上面的事件,並.on()用另外一個  .pipe()調用替換該  調用:

const zlib = require('zlib'); 
const file = process.argv [2]; 

const {Transform} = require('stream'); 

const reportProgress = new Transform({ 
  transform(chunk,encoding,callback){ 
    process.stdout.write('。'); 
    callback(null,chunk); 
  } 
}); 

fs.createReadStream(file)
  .pipe(zlib.createGzip())。
  pipe(reportProgress)
   .pipe(fs.createWriteStream(file +'。。'))。
  on('finish',()=> console.log( '完成'));const fs = require('fs'); 
const zlib = require('zlib'); 
const file = process.argv [2]; 

const {Transform} = require('stream'); 

const reportProgress = new Transform({ 
  transform(chunk,encoding,callback){ 
    process.stdout.write('。'); 
    callback(null,chunk); 
  } 
}); 

fs.createReadStream(file)
  .pipe(zlib.createGzip())。
 pipe(reportProgress)
   .pipe(fs.createWriteStream(file +'。。'))。
  on('finish',()=> console.log( '完成'));

reportProgress流是一個簡單的直通流,但它也會將進度報告給標準輸出。請注意我如何使用函數中的第二個參數callback()來推送transform()方法中的數據。這至關於首先推送數據。

組合流的應用是無止境的。例如,若是咱們須要在gzip以前或以後加密文件,咱們須要作的就是按照咱們須要的確切順序管道另外一個轉換流。咱們可使用Node的crypto模塊:

// ...const crypto = require('crypto'); // ...
  .pipe(zlib.createGzip())。
  pipe(crypto.createCipher('aes192','a_secret'))。
   pipe(reportProgress)
  .pipe(fs.createWriteStream(file +'.zz') )
  .on('finish',()=> console.log('完成'));fs.createReadStream(file)
  .pipe(zlib.createGzip())。
 pipe(crypto.createCipher('aes192','a_secret'))。    pipe(reportProgress)
  .pipe(fs.createWriteStream(file +'.zz') )
  .on('finish',()=> console.log('完成'));

上面的腳本壓縮而後加密傳遞的文件,只有擁有祕密的人才能使用輸出的文件。咱們沒法使用普通的解壓縮實用程序解壓縮此文件,由於它已加密。

要實際可以解壓縮上面腳本壓縮的任何內容,咱們須要以相反的順序使用相反的流加密和zlib,這很簡單:

  .pipe(加密createDecipher( 'AES192', 'a_secret'))
  .pipe(ZLIB。createGunzip())
  .pipe(reportProgress)
  .pipe(fs.createWriteStream(file.slice(0, - 3)))。
  on('finish',()=> console.log('Done'));fs.createReadStream(文件)
  .pipe(加密createDecipher( 'AES192', 'a_secret'))
  .pipe(ZLIB。createGunzip())
  .pipe(reportProgress)
  .pipe(fs.createWriteStream(file.slice(0, - 3)))。
  on('finish',()=> console.log('Done'));

假設傳遞的文件是壓縮版本,上面的代碼將從中建立一個讀取流,將其傳遞到加密createDecipher()流(使用相同的祕密),將其輸出createGunzip()傳遞到zlib 流,而後將內容寫回沒有擴展部分的文件。

這就是我對這個話題的所有見解。謝謝閱讀!直到下一次!


學習反應仍是節點?查看個人書:

相關文章
相關標籤/搜索