Node.js-striimit: FileStream, putket ja tapahtumat

⚡ Älykäs yhteenveto

Node.js lukee ja kirjoittaa dataa pienissä osissa sen sijaan, että se lataisi kokonaisia ​​tiedostoja muistiin. Tällä sivulla selitetään tiedostovirran luominen, ketjutus, vastapaineen käsittely ja tapahtumamalli, joka pitää laajamittaisen tiedonsiirron tehokkaana.

  • ???? Striimityypit: Node.js järjestää jokaisen virran luettavaksi, kirjoitettavaksi, kaksipuoliseksi tai muunnettavaksi, ja jokainen Node.js-API palauttaa yhden näistä neljästä tyypistä.
  • 📂 Tiedostovirta: fs.createReadStream() ja fs.createWriteStream() lukevat ja kirjoittavat tiedostoja paloina lataamatta koko tiedostoa muistiin.
  • 🔗 Piping: pipe()-metodi yhdistää luettavan virran suoraan kirjoitettavaan virtaan ja siirtää dataa niiden välillä automaattisesti.
  • 🇧🇷 Vastapaine: write() palauttaa arvon false, kun sen puskuri on täynnä, ja drain-tapahtuma ilmoittaa, milloin kirjoittamista on turvallista jatkaa.
  • 📡 Striimaustapahtumat: data, end, error ja finish merkitsevät striimin elinkaaren tärkeimmät hetket ja ohjaavat suurinta osaa striimin käsittelyyn liittyvästä koodista.
  • ⚡ Mukautetut tapahtumat: Tapahtumamoduuli ja EventEmitter antavat minkä tahansa Node.js-sovelluksen määritellä, lähettää ja kuunnella omia nimettyjä tapahtumiaan.
  • 🛠️ Kuuntelijan ohjaus: once(), listenerCount() ja newListener-tapahtuma hallitsevat sitä, kuinka monta kertaa käsittelijä suoritetaan ja miten kuuntelijat toimivat. tractäikärpänen.

Node.js:n striimien tyypit

Jokainen Node.js:n paljastama striimi kuuluu johonkin neljästä perustyypistä, ja tietyn API:n palauttaman tyypin tietäminen selittää, miten striimi käyttäytyy ennen koodin kirjoittamista. Node.js:n mukana toimitettava striimimoduuli määrittelee nämä luokat, ja tässä artikkelissa käytetyt tiedosto-, verkko- ja pakkaus-API:t on kaikki rakennettu niiden päälle.

Suoratoiston tyyppi Datan suunta Esimerkki API:sta Yhteinen käyttötapaus
Readable Vain lähde (data virtaa ulos) fs.createReadStream() Tiedoston lukeminen, HTTP-pyynnön rungon vastaanottaminen
Kirjoitettavissa Vain kohde (data virtaa sisään) fs.createWriteStream() Tiedoston kirjoittaminen, HTTP-vastauksen lähettäminen
Duplex Molempiin suuntiin, itsenäisesti net.Socket (TCP-socket) Kaksisuuntainen verkkoviestintä
Muuttaa Molempiin suuntiin, dataa muokataan siirron aikana zlib.createGzip() Datan pakkaaminen tai koodaus prosessin keskivaiheilla

Luettava virta ja kirjoitettava virta ovat tässä artikkelissa käytetyt kaksi rakennuspalikkaa; molemmat myös käynnistävät HTTP-verkkopalvelin Node.js:ssä. Dupleksivirta yhdistää molemmat roolit yhteen objektiin, kun taas muunnosvirta on dupleksivirta, jonka lähtö muokkaa syötettä, kuten gzip-pakkausta virran keskellä.

Tiedostovirta Node.js:ssä

Kun neljä striimiluokkaa on määritelty, artikkelin loppuosa rakentaa tiedostonsiirron luettavien ja kirjoitettavien striimi-APIen avulla.

Node käyttää laajasti tietovirtoja tiedonsiirtomekanismina.

Kun esimerkiksi tulostat konsoliin jotain käyttämällä console.log-funktiota, käytät itse asiassa tietovirtaa tietojen lähettämiseen konsoliin.

Node.js pystyy myös suoratoistamaan tietoja tiedostoista, jotta niitä voidaan lukea ja kirjoittaa oikein. Tarkastellaan nyt esimerkkiä siitä, kuinka voimme käyttää virtoja tiedostojen lukemiseen ja kirjoittamiseen. Meidän on noudatettava alla mainittuja vaiheita tässä esimerkissä

Vaihe 1) Luo tiedosto nimeltä data.txt, joka sisältää alla olevat tiedot. Oletetaan, että tämä tiedosto on tallennettu paikallisen koneemme D-asemalle.

Node.js:n opetusohjelma

esittely

Tapahtumat

Generators

Datayhteys

Jasmiinin käyttö

Vaihe 2) Kirjoita asiaankuuluva koodi, joka käyttää tietovirtoja datan lukemiseen tiedostosta.

Tiedostovirta Node.js:ssä

var fs = require("fs");
var stream;
stream = fs.createReadStream("D://data.txt");

stream.on("data", function(data) {
    var chunk = data.toString();
    console.log(chunk);
}); 

Code Selitys:-

  1. Ensin meidän on sisällytettävä "fs"-moduulit, jotka sisältävät kaikki toiminnot, joita tarvitaan streamien luomiseen.
  2. Seuraavaksi luomme luettavan virran menetelmällä createReadStream. Syötteenä annamme data.txt-tiedostomme sijainnin.
  3. Steam.on-funktio on tapahtumakäsittelijä, ja siinä määritämme ensimmäisen parametrin dataksi. Tämä tarkoittaa, että aina kun dataa tulee tiedostosta, suorita takaisinsoittotoiminto. Meidän tapauksessamme määrittelemme takaisinsoittotoiminnon, joka suorittaa 2 perusvaihetta. Ensimmäinen on muuntaa tiedostosta luetut tiedot merkkijonoksi. Toinen olisi lähettää muunnettu merkkijono tulosteena konsoliin.
  4. Otamme jokaisen tietovirrasta luetun datapalan ja muunnamme sen merkkijonoksi.
  5. Lopuksi lähetämme jokaisen muunnetun merkkijonon tulosteen konsoliin.

lähtö:

Tiedostovirta Node.js:ssä

  • Jos koodi suoritetaan oikein, näet yllä olevan tulosteen konsolissa. Tämä tulos on sama kuin data.txt-tiedostossa.

Kirjoittaminen tiedostoon

Samalla tavalla kuin luomme lukuvirran, voimme myös luoda kirjoitusvirran tietojen kirjoittamiseksi tiedostoon. Luodaan ensin tyhjä tiedosto nimeltä data.txt ilman sisältöä. Oletetaan, että tämä tiedosto on sijoitettu tietokoneemme D-asemaan.

Alla oleva koodi näyttää, kuinka voimme kirjoittaa tietoja tiedostoon.

Tiedostovirta Node.js:ssä

var fs = require("fs");
var stream;
stream = fs.createWriteStream("D://data.txt");

stream.write("Tutorial on Node.js")
stream.write("Introduction")
stream.write("Events")
stream.write("Generators")
stream.write("Data Connectivity")
stream.write("Using Jasmine") 

Code Selitys:-

  1. Luomme kirjoitettavan streamin menetelmällä createWriteStream. Syötteenä annamme data.txt-tiedostomme sijainnin.
  2. Seuraavaksi käytimme stream.write-menetelmää eri tekstirivien kirjoittamiseen tekstitiedostoomme. Stream huolehtii näiden tietojen kirjoittamisesta data.txt-tiedostoon.

Jos avaat data.txt-tiedoston, näet nyt tiedostossa seuraavat tiedot

Node.js:n opetusohjelma

esittely

Tapahtumat

Generators

Datayhteys

Käyttäminen Jasmiini

Putket Node.js:ssä

Virtojen lukeminen ja kirjoittaminen erikseen on hyödyllistä, mutta Node.js antaa myös luettavan virran muodostaa suoran yhteyden kirjoitettavaan virraan, siirtäen dataa ilman manuaalista puskurointikoodia.

Solmusovellusten sisällä virrat voidaan yhdistää pipe()-menetelmällä, jossa on kaksi argumenttia:

  • Vaadittu kirjoitettava tietovirta, joka toimii tietojen määränpäänä ja
  • Valinnainen objekti, jota käytetään vaihtoehtojen välittämiseen.

Tyypillinen esimerkki putkien käytöstä, jos haluat siirtää tietoja tiedostosta toiseen.

Katsotaanpa siis esimerkkiä siitä, kuinka voimme siirtää tietoja tiedostosta toiseen putkien avulla.

Vaihe 1) Luo tiedosto nimeltä datainput.txt, joka sisältää alla olevat tiedot. Oletetaan, että tämä tiedosto on tallennettu paikallisen koneemme D-asemalle.

Node.js:n opetusohjelma

esittely

Tapahtumat

Generators

Datayhteys

Jasmiinin käyttö

Vaihe 2) Luo tyhjä tyhjä tiedosto nimeltä dataOutput.txt ja sijoita se paikallisen koneen D-asemaan.

Vaihe 3) Kirjoita alla oleva koodi suorittaaksesi tiedonsiirron datainput.txt-tiedostosta dataOutput.txt-tiedostoon.

Putket Node.js:ssä

var fs = require("fs");
var readStream = fs.createReadStream("D://datainput.txt");
var writeStream = fs.createWriteStream("D://dataOutput.txt");
readStream.pipe(writeStream);

Code Selitys:-

  1. Luomme ensin "readstream" datainput.txt-tiedostoomme, joka sisältää kaikki tietomme, jotka on siirrettävä uuteen tiedostoon.
  2. Meidän on sitten luotava "kirjoitusvirta" dataOutput.txt-tiedostoomme, joka on tyhjä tiedostomme ja datainput.txt-tiedoston tietojen siirron kohde.
  3. Käytämme sitten pipe-komentoa tietojen siirtämiseen lukuvirrasta kirjoitusvirtaan. Putkikomento ottaa kaikki lukuvirtaan tulevat tiedot ja työntää sen kirjoitusvirtaan.

Jos nyt avaat dataOutput.txt-tiedoston, näet kaikki datainput.txt-tiedostossa olleet tiedot.

Striimaustapahtumat ja vastapaine Node.js:ssä

Kuten yllä on esitetty, pipe()-funktion kutsuminen piilottaa kaksi skaalautuvasti tärkeää mekaniikkaa: jokaisen virran lähettämät tapahtumat ja vastapainejärjestelmän, joka estää nopeaa lähdettä ylikuormittamasta hidasta kohdetta.

  • tiedot: luettava virta lähettää sen joka kerta, kun uusi lohko on saatavilla.
  • end: lähetetään, kun luettava virta on toimittanut viimeisen osan.
  • suorittaa loppuun: kirjoitettavan virran lähettämä, kun kaikki jonossa olevat tiedot on tyhjennetty.
  • virhe: kumman tahansa virtatyypin lähettämä virheen sattuessa; käsittele se aina tai prosessi kaatuu.

Alla oleva esimerkki kuuntelee kaikkia neljää tapahtumaa ja hallitsee samalla manuaalisesti vastapainetta, samaa toimintaa kuin pipe() tarjoaa automaattisesti.

var fs = require("fs");
var readStream = fs.createReadStream("D://data.txt");
var writeStream = fs.createWriteStream("D://dataOutput.txt");

readStream.on("data", function(chunk) {
    var ok = writeStream.write(chunk);
    if (!ok) {
        readStream.pause();
    }
});

writeStream.on("drain", function() {
    readStream.resume();
});

readStream.on("end", function() {
    writeStream.end();
});

writeStream.on("finish", function() {
    console.log("Write completed.");
});

readStream.on("error", function(err) {
    console.log(err);
});

Vastapaine tapahtuu, kun luettava virta tuottaa dataa nopeammin kuin kirjoitettava virta voi käsitellä sitä. write()-metodi palauttaa arvon false, kun sen puskuri täyttyy, joten yllä oleva koodi keskeyttää lähteen, kunnes kohde lähettää tyhjennystapahtuman, joka viestii, että jatkaminen on turvallista. pipe() suorittaa tämän syklin automaattisesti.

⚠ Vinkki: Tuotannossa on parempi käyttää stream-moduulin pipeline()-funktiota kuin manuaalista pipe()-ketjua, koska se välittää virheet jokaisesta streamista ja sulkee tiedostokahvat automaattisesti.

Tapahtumat Node.js:ssä

Striimit ovat yksi esimerkki Node.js:n laajemmasta tapahtumapohjaisesta suunnittelusta. Artikkelin loppuosa tarkastelee tapahtumien yleistä toimintaa käyttäen samoja EventEmitter-luokkaa, joihin striimit perustuvat sisäisesti.

Tapahtumat ovat yksi Node.js:n avainkäsitteistä, ja joskus Node.js:ää kutsutaan tapahtumalähtöiseksi viitekehykseksi.

Periaatteessa tapahtuma on jotain, joka tapahtuu. Jos esimerkiksi muodostetaan yhteys tietokantaan, tietokantayhteystapahtuma laukeaa. Tapahtumapohjaisen ohjelmoinnin tarkoituksena on luoda toimintoja, jotka käynnistyvät, kun tietyt tapahtumat laukaistaan.

Katsotaanpa perusesimerkkiä tapahtuman määrittämisestä Node.js:ssä.

Aiomme luoda tapahtuman nimeltä 'data_received'. Kun tämä tapahtuma laukeaa, teksti "data vastaanotettu" lähetetään konsoliin.

Tapahtumat Node.js:ssä

var events = require('events');
var eventEmitter = new events.EventEmitter();
eventEmitter.on('data_received', function() {
    console.log('data received succesfully.');
});

eventEmitter.emit('data_received'); 

Code Selitys:-

  1. Käytä vaadi-funktiota sisällyttääksesi "tapahtumat"-moduulin. Tämän moduulin avulla voit luoda tapahtumia Node.js:ssä.
  2. Luo uusi tapahtumien lähettäjä. Tätä käytetään tapahtuman sitomiseen, joka meidän tapauksessamme on "data_received" takaisinsoittotoimintoon, joka määritellään vaiheessa3.
  3. Määrittelemme tapahtumapohjaisen funktion, joka sanoo, että jos "data_received" -tapahtuma laukeaa, meidän pitäisi tulostaa teksti "data_received" konsoliin.
  4. Lopuksi meillä on tapahtumamme manuaalinen laukaisu käyttämällä eventEmiter.emit-toimintoa. Tämä käynnistää data_received -tapahtuman.

Kun ohjelma ajetaan, teksti "data vastaanotettu" lähetetään konsoliin alla olevan kuvan mukaisesti.

Tapahtumat Node.js:ssä

Tapahtumien lähettäminen

Tapahtumien lähettämisen ja kuuntelun lisäksi Node.js tarjoaa lisämenetelmiä kuuntelijoiden rekisteröinnin ja tarkastuksen hallintaan.

Tapahtumia määritettäessä on olemassa erilaisia ​​menetelmiä tapahtumille, joita voidaan kutsua. Tämä aihe keskittyy tarkastelemaan jokaista niistä yksityiskohtaisesti.

  1. Kertaluonteiset tapahtumakäsittelijät

Joskus saatat olla kiinnostunut reagoimaan tapahtumaan vasta ensimmäisen kerran. Näissä tilanteissa voit käyttää Once()-menetelmää.

Katsotaanpa, kuinka voimme hyödyntää kerta-menetelmää tapahtumakäsittelijöille.

Tapahtumien lähettäminen

Code Selitys:-

  1. Tässä käytämme "once"-menetelmää sanoaksemme, että tapahtuman "data_received" takaisinkutsutoiminto tulisi suorittaa vain kerran.
  2. Tässä käynnistämme manuaalisesti data_received -tapahtuman.
  3. Kun 'data_received' -tapahtuma käynnistyy uudelleen, tällä kertaa mitään ei tapahdu. Tämä johtuu ensimmäisestä vaiheesta, jossa sanoimme, että tapahtuma voidaan käynnistää vain kerran.

Jos koodi suoritetaan oikein, lokin tulos on 'data_received onnistui'. Tämä viesti näkyy vain kerran konsolissa.

  1. Tapahtumien kuuntelijoiden tarkastus

Milloin tahansa sen elinkaaren aikana tapahtumalähettimellä voi olla nolla tai useampi kuuntelija. Kunkin tapahtumatyypin kuuntelijat voidaan tarkastaa monella tavalla.

Jos olet kiinnostunut vain määrittämään liitteenä olevien kuuntelijoiden lukumäärän, etsi vain EventEmitter.listenerCount()-metodia.

(Huomautus: Kuuntelijat ovat tärkeitä, koska pääohjelman pitäisi tietää, jos kuuntelijoita lisätään tapahtumaan lennossa, muuten ohjelma ei toimi, koska lisää kuuntelijoita kutsutaan.)

Tapahtumien lähettäminen

Code Selitys:-

  1. Määrittelemme eventEmitter-tyyppiä, jota tarvitaan tapahtumaan liittyvien menetelmien käyttöön.
  2. Sitten määrittelemme objektin nimeltä emitter, jota käytetään tapahtumakäsittelijöiden määrittelemiseen.
  3. Luomme 2 tapahtumakäsittelijää, jotka periaatteessa eivät tee mitään. Tämä on pidetty esimerkissämme yksinkertaisena vain sen osoittamiseksi, kuinka listenerCount-menetelmä toimii.
  4. Nyt kun kutsut listenerCount-menetelmän data_received-tapahtumassamme, se lähettää konsolin lokiin tähän tapahtumaan liitettyjen tapahtumaseurainten määrän.

Jos koodi suoritetaan oikein, arvo 2 näkyy konsolin lokissa.

  1. UusiListener-tapahtuma

Joka kerta kun uusi tapahtumakäsittelijä rekisteröidään, tapahtumalähettäjä lähettää newListener-tapahtuman. Tätä tapahtumaa käytetään uusien tapahtumakäsittelijöiden havaitsemiseen. Käytät yleensä newListener-tapahtumaa, kun joudut allokoimaan resursseja tai suorittamaan toimenpiteitä jokaiselle uudelle tapahtumakäsittelijälle.

Tapahtumien lähettäminen

var events = require('events');
var eventEmitter = events.EventEmitter;
var emitter = new eventEmitter();
emitter.on("newListener", function(eventName, listener) {
    console.log("Added listener for " + eventName + " events");
});
emitter.on('data_received', function() {});
emitter.on('data_received', function() {}); 

Code Selitys:-

  1. Luomme uutta tapahtumakäsittelijää "newListener" -tapahtumalle. Joten aina kun uusi tapahtumakäsittelijä rekisteröidään, teksti "Lisätty kuuntelija" + tapahtuman nimi näkyy konsolissa.
  2. Täällä kirjoitamme konsoliin tekstiä "Lisätty kuuntelija" + tapahtuman nimi jokaiselle rekisteröidylle tapahtumalle.
  3. Määrittelemme 2 tapahtumakäsittelijää tapahtumallemme 'data_received'.

Jos yllä oleva koodi suoritetaan oikein, alla oleva teksti näytetään konsolissa. Se vain osoittaa, että "newListener" -tapahtumakäsittelijä laukaistiin kahdesti.

Lisätty kuuntelija data_received -tapahtumille

Lisätty kuuntelija data_received -tapahtumille

UKK

fs.readFile() lataa koko tiedoston muistiin ennen sen palauttamista, kun taas fs.createReadStream() toimittaa tiedoston pienissä paloissa niiden saapuessa. Virrat käyttävät paljon vähemmän muistia suurille tiedostoille ja antavat käsittelyn alkaa ennen lukemisen päättymistä.

Kyllä. Kutsu .pipe()-funktiota kerran määränpäätä kohden, esimerkiksi readStream.pipe(destinationA) ja sen jälkeen readStream.pipe(destinationB). Node.js toimittaa samat lohkot jokaiseen määränpäähän, ja jokainen kirjoitettava virta hallitsee edelleen omaa vastapainettaan itsenäisesti.

Kyllä. Putkita luettava striimi muunnosvirran, kuten zlib.createGzip(), läpi ja sitten kirjoitettavaan striimiin: readStream.pipe(zlib.createGzip()).pipe(writeStream). Tiedosto pakkaa itsensä striimauksen aikana lataamatta koko tiedostoa muistiin.

Ne voivat luonnostella toimivan readStreamin ja pipe-pohjan nopeasti, mutta niistä usein puuttuu virheiden käsittelijä ja vastapaineen käsittely. Tarkista aina tekoälyn luoma striimikoodi molempien aukkojen varalta ennen sen käyttöä tuotannossa.

Ne voivat ehdottaa todennäköisiä syitä, kuten puuttuvaa tyhjennyskuuntelijaa tai käsittelemätöntä virhetapahtumaa, mutta todellisen syyn vahvistaminen edellyttää silti striimin tilan ja sovelluslokien manuaalista tarkistamista.

Tiivistä tämä viesti seuraavasti: