Node.js oqimlari: bilishingiz kerak bo'lgan hamma narsa

Rasm manbai
Yangilanish: Ushbu maqola endi mening "Node.js asoslaridan tashqari" kitobimning bir qismi.
Ushbu tarkibning yangilangan versiyasini va Node haqida ko'proq ma'lumotni jscomplete.com/node-beyond-basics saytida o'qing.

Node.js oqimlari bilan ishlash qiyin bo'lganligi va hatto tushunish qiyinligi bilan tanilgan. Xo'sh, men siz uchun yaxshi yangilik oldim - endi bunday bo'lmaydi.

Yillar davomida ishlab chiquvchilar oqimlar bilan ishlashni osonlashtirish uchun yagona maqsad bilan u erda juda ko'p paketlarni yaratdilar. Ammo ushbu maqolada men mahalliy Node.js stream API-ga e'tibor qaratmoqchiman.

"Oqimlar - bu Node eng yaxshi va eng noto'g'ri tushunilgan narsa."
- Dominik Tarr

Oqimlar aniq nima?

Oqimlar bu ma'lumotlar to'plami - xuddi qatorlar yoki satrlar kabi. Farqi shundaki, oqimlar bir vaqtning o'zida mavjud bo'lmasligi mumkin va ular xotiraga sig'ishi shart emas. Bu katta hajmdagi ma'lumotlar yoki bir vaqtning o'zida tashqi manbadan keladigan ma'lumotlar bilan ishlashda oqimlarni juda kuchli qiladi.

Biroq, oqim nafaqat katta ma'lumotlar bilan ishlashni anglatadi. Shuningdek, ular bizning kodimizda moslashuvchanlik kuchini beradi. Linux-ning boshqa kichik buyruqlarini o'tkazish orqali kuchli linux buyruqlarini tuzishimiz singari, biz Node-da oqimlar bilan ham xuddi shunday qilishimiz mumkin.

Linux buyruqlari bilan moslik
const grep = ... // grep chiqishi uchun oqim
const wc = ... // wc kiritish uchun oqim
grep.pipe (dc)

Node-da o'rnatilgan ko'plab modullar oqim interfeysini amalga oshiradi:

Skrinshot mening Pluralsight kursidan olingan - Advanced Node.js

Yuqoridagi ro'yxatda mahalliy Node.js ob'ektlari uchun bir nechta misollar mavjud, ular o'qilishi va yozilishi mumkin bo'lgan oqimlar. Ushbu ob'ektlarning ba'zilari TCP soketlari, zlib va ​​kripto oqimlari kabi o'qiladigan va yoziladigan oqimlardir.

Ob'ektlar ham chambarchas bog'liqligiga e'tibor bering. HTTP-javob mijozda o'qilishi mumkin bo'lgan oqim bo'lsa-da, bu serverdagi yoziladigan oqim. Buning sababi, HTTP holatida biz asosan bitta ob'ektdan o'qiymiz (http.IncomingMessage) va boshqasiga (http.ServerResponse) yozamiz.

Bola jarayonlari haqida gap ketganda stdio oqimlari (stdin, stdout, stderr) teskari oqim turlariga qanday ega ekanligiga e'tibor bering. Bu asosiy oqim stdio oqimlaridan ushbu oqimlarga va undan quvurlarga o'tishning juda oson usulini ta'minlaydi.

Amaliy misol

Nazariya ajoyib, lekin ko'pincha 100% ishonchli emas. Xotirani iste'mol qilishga kelganda oqimlarning farqini ko'rsatadigan misolni ko'rib chiqaylik.

Avval katta fayl yarataylik:

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

uchun (bo'lsin i = 0; i <= 1e6; i ++) {
  file.write ('Lorem ipsum zavol amet, adipisising conveetet conentetate to labour va dolour magna aliqua). Eng minim veniam bilan ishlang, nostrud ullamo mehnatini bekor qiling va juda ko'p ishlarni bajaring. Qayta ishlashda o'zingizni bemalol esse cillum dolore va fugiat nulla pariatur (bundan mustasno holatlardan tashqari, ishdan bo'shatish kerak emas.
}

file.end ();

Men o'sha katta faylni yaratishda foydalangan narsalarimga qarang. Yozib olinadigan oqim!

Fs moduli oqim interfeysi yordamida fayllarni o'qish va yozish uchun ishlatilishi mumkin. Yuqoridagi misolda biz big.file-ga yoziladigan oqim orqali 1 million qatorni pastadir bilan yozmoqdamiz.

Yuqoridagi skriptni ishga tushirish taxminan 400 MB hajmdagi faylni yaratadi.

Bu erda faqat big.file-ga xizmat qilish uchun mo'ljallangan oddiy "Node" veb-server mavjud:

const fs = zarur ('fs');
const server = talab qilish ('http'). createServer ();

server.on ('request', (req, res) => {
  fs.readFile ('./ big.file', (xato, ma'lumotlar) => {
    if (err) err otmoq;
  
    res.end (ma'lumotlar);
  });
});

server.listen (8000);

Server so'rov yuborganda, fs.readFile asenkron usuldan foydalanib, katta faylga xizmat qiladi. Ammo, biz voqea qatorini yoki biror narsani to'sib qo'yayotganimiz kabi emas. Hammasi yaxshi, to'g'rimi? To'g'ri?

Xo'sh, serverni ishga tushirganda, unga ulanishda va xotirani kuzatishda nima bo'lishini ko'rib chiqaylik.

Men serverni ishlatsam, u 8,7 MB normal xotira hajmi bilan boshlangan:

Keyin men serverga ulandim. Iste'mol qilingan xotira bilan nima bo'lganiga e'tibor bering:

Vau - xotira sarfi 434,8 Mb ga etdi.

Javob ob'ektiga yozmasdan oldin biz asosan big.file tarkibini xotirada saqlaymiz. Bu juda samarasiz.

HTTP javob ob'ekti (yuqoridagi kodda ko'rsatilgan) ham yoziladigan oqimdir. Agar bizda big.file tarkibini ifoda etadigan oqimli oqim bo'lsa, biz ikkalasini bir-birimizga osib qo'yamiz va ~ 400 MB xotira sarflamasdan bir xil natijaga erishamiz.

Node fs moduli bizga createReadStream usulidan foydalangan holda har qanday fayl uchun o'qilishi mumkin bo'lgan oqimni berishi mumkin. Buni javob ob'ektiga yuborishimiz mumkin:

const fs = zarur ('fs');
const server = talab qilish ('http'). createServer ();

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

server.listen (8000);

Endi ushbu serverga ulanganda sehrli narsa yuz beradi (xotira sarfini ko'rib chiqing):

Nima bulyapti?

Mijoz ushbu katta faylni so'raganda, biz uni bir vaqtning o'zida bitta qismga to'ldiramiz, ya'ni biz uni umuman xotirada bufer qilmaymiz. Xotiradan foydalanish hajmi 25 MB ga oshdi va aynan shu.

Siz ushbu misolni uning chegaralariga etkazishingiz mumkin. Big.file faylini bir million o'rniga 5 million chiziq bilan yangilang, bu faylni 2 GB dan oshiqroq qiladi va bu Node-ning standart bufer chegarasidan ancha katta.

Agar siz fs.readFile-dan foydalanib ushbu faylga xizmat ko'rsatishga harakat qilsangiz, odatdagidek qila olmaysiz (chegaralarni o'zgartirishingiz mumkin). Ammo fs.createReadStream-da, 2 Gb ma'lumotni so'rovchiga uzatishda hech qanday muammo bo'lmaydi va eng yaxshisi, operatsion xotiradan foydalanish deyarli bir xil bo'ladi.

Oqimlarni hoziroq bilishga tayyormisiz?

Ushbu maqola mening Node.js-ga bag'ishlangan Pluralsight kursimning bir qismi. Men shu kabi tarkibni video formatida joylashtiraman.

Oqim 101

Node.js-da to'rtta asosiy oqim turi mavjud: O'qiladigan, Yoziladigan, Ikki tomonlama va Transformatsiya oqimlari.

  • O'qilishi mumkin bo'lgan oqim - bu ma'lumot iste'mol qilinishi mumkin bo'lgan manba uchun mavhumlik. Bunga misol fs.createReadStream usuli.
  • Yozib olinadigan oqim bu ma'lumot yozilishi mumkin bo'lgan manzil uchun mavhumdir. Bunga misol fs.createWriteStream usuli.
  • Ikki tomonlama oqim ham o'qilishi mumkin, ham yozilishi mumkin. Bunga misol TCP soketidir.
  • Transformatsiya oqimi asosan yozilgan va o'qilganidek ma'lumotlarni o'zgartirish yoki o'zgartirish uchun ishlatilishi mumkin bo'lgan ikki tomonlama oqimdir. Bunga misol gzip yordamida ma'lumotlarni siqish uchun zlib.createGzip oqimi. Transformatsiya oqimini funktsiyani deb hisoblashingiz mumkin, bu erda kirish yoziladigan oqim qismi va chiqish o'qilishi mumkin bo'lgan oqim qismi mavjud. Siz "oqimlar" deb nomlangan transformatsiya oqimlarini ham eshitishingiz mumkin.

Barcha oqimlar EventEmitter-ning namunalari. Ular ma'lumotlarni o'qish va yozish uchun ishlatilishi mumkin bo'lgan hodisalarni chiqaradilar. Biroq, biz oqim usuli ma'lumotlarini quvur usuli yordamida sodda tarzda iste'mol qilishimiz mumkin.

Quvurlar usuli

Siz eslab qolishingiz kerak bo'lgan sehrli chiziq:

readableSrc.pipe (WritableDest)

Ushbu sodda chiziqda biz o'qiladigan oqimning natijasini - ma'lumotlar manbasini, yozib olinadigan oqimning manbai sifatida - yo'nalishni bog'laymiz. Manba o'qilishi kerak bo'lgan oqim bo'lishi kerak va boradigan joy yozib olinadigan bo'lishi kerak. Albatta, ularning ikkalasi ham oqim / transformatsiya bo'lishi mumkin. Aslida, agar biz ikki tomonlama oqimga tushadigan bo'lsak, Linux-dagi kabi qo'ng'iroqlar zanjirini uzatishimiz mumkin:

o'qish mumkinSrc
  .pipe (transformStream1)
  .pipe (transformStream2)
  .pipe (finalWrtitableDest)

Quvur usuli yuqoridagi zanjirni bajarishga imkon beradigan maqsad oqimini qaytaradi. A (o'qilishi mumkin), b va c (dupleks) va d (yozilishi mumkin) oqimlari uchun biz:

(b) .pipe (c) .pipe (d).
# Ga teng:
(b) quvur
b.pipe (c)
t.pipe (d)
# Linuxda quyidagiga teng keladimi:
$ a | b | c | d

Quvurlar usuli oqimlarni iste'mol qilishning eng oson usuli hisoblanadi. Odatda quvur usulidan foydalanish yoki oqimlarni voqealar bilan birga iste'mol qilish tavsiya etiladi, ammo bu ikkalasini aralashtirishdan saqlaning. Odatda siz quvur usulidan foydalansangiz, voqealarni ishlatishingiz shart emas, lekin agar siz oqimlarni ko'proq odatiy usulda iste'mol qilishingiz kerak bo'lsa, voqealar bunga yo'l bo'lishi mumkin.

Oqim tadbirlari

O'qilishi mumkin bo'lgan oqim manbasidan o'qish va yozib olinadigan joyga yozish bilan bir qatorda, quvur usuli avtomatik ravishda yo'l davomida bir nechta narsalarni boshqaradi. Masalan, u xatolar, fayllarning oxiri va bitta oqim ikkinchisiga nisbatan sekinroq yoki tezroq bo'lgan holatlarga ishlov beradi.

Biroq, oqimlarni to'g'ridan-to'g'ri hodisalar bilan birga iste'mol qilish mumkin. Bu erda ma'lumotlarni o'qish va yozish uchun trubka usuli asosan bajaradigan voqea-ekvivalenti soddalashtirilgan kodi:

# o'qiladigan.pipe (yozilishi mumkin)
readable.on ('data', (chunk) => {
  Writing.write (chunk);
});
readable.on ('end', () => {
  Writable.end ();
});

O'qiladigan va yozib olinadigan oqimlarda ishlatilishi mumkin bo'lgan muhim voqealar va funktsiyalar ro'yxati:

Skrinshot mening Pluralsight kursimdan olingan - Advanced Node.js

Hodisalar va funktsiyalar qandaydir tarzda bog'liq, chunki ular odatda birgalikda ishlatiladi.

O'qiladigan oqimdagi eng muhim voqealar:

  • Oqim iste'molchilarga uzatiladigan ma'lumotlar uzatilganda tarqatiladigan ma'lumotlar hodisasi
  • Oqimdan boshqa ma'lumotlar iste'mol qilinmasa chiqariladigan yakuniy voqea.

Yoziladigan oqimdagi eng muhim voqealar:

  • Drenaj hodisasi, bu yozib olinadigan oqim ko'proq ma'lumotlarni qabul qilishi mumkinligini anglatadi.
  • Barcha ma'lumotlar asosiy tizimga yuborilganda chiqariladigan tugatish hodisasi.

Oqimlardan odatiy va optimallashtirilgan foydalanish uchun voqealar va funktsiyalarni birlashtirish mumkin. O'qilishi mumkin bo'lgan oqimni iste'mol qilish uchun biz quvurlarni tozalash / o'chirish usullarini yoki o'qish / unshift / rezyume usullarini ishlatamiz. Yozib olinadigan oqimni iste'mol qilish uchun biz uni quvur / ochish joyiga aylantiramiz yoki unga yozish usuli bilan yozamiz va biz tugagandan so'ng tugatish usulini chaqirishimiz mumkin.

O'qiladigan oqimlarning to'xtatib turadigan va oqadigan rejimlari

O'qish mumkin bo'lgan oqim ikkita asosiy usulga ega, bu ularni iste'mol qilishimizga ta'sir qiladi.

  • Ular pauza rejimida bo'lishi mumkin
  • Yoki oqim rejimida

Ushbu rejimlarga ba'zan tortish va surish rejimlari deyiladi.

O'qilishi mumkin bo'lgan barcha oqimlar sukut rejimida boshlanadi, ammo ularni osongina oqim bilan almashtirish mumkin va kerak bo'lganda orqaga qaytarish mumkin. Ba'zida kommutatsiya avtomatik ravishda sodir bo'ladi.

O'qilishi mumkin bo'lgan oqim to'xtatib turish rejimida bo'lsa, biz o'qish () usulidan foydalanib, oqim orqali o'qish uchun foydalanishimiz mumkin, ammo oqim rejimida o'qiladigan oqim uchun ma'lumotlar doimiy ravishda oqadi va biz voqealarni tinglashimiz kerak. iste'mol qil.

Oqim rejimida hech qanday iste'molchi topilmasa, ma'lumotlar aslida yo'qolishi mumkin. Shuning uchun, oqim rejimida o'qiladigan oqim bo'lsa, bizda ma'lumotlarni boshqarish uchun ishlov beruvchiga ehtiyoj seziladi. Aslida, shunchaki ma'lumot voqealari ishlov beruvchisini qo'shib qo'yish to'xtatilgan oqimni oqim rejimiga o'tkazadi va ma'lumotlar hodisalarini boshqaruvchisi olib tashlansa, oqim qayta to'xtatilgan rejimga o'tadi. Bularning ba'zilari oldingi Node oqimlari interfeysi bilan orqaga muvofiqligi uchun qilingan.

Ushbu ikki oqim rejimini qo'lda almashtirish uchun rezyume () va pauza () usullaridan foydalanishingiz mumkin.

Skrinshot mening Pluralsight kursidan olingan - Advanced Node.js

Quvurlar usuli yordamida o'qiladigan oqimlarni iste'mol qilganda, biz ushbu rejimlar haqida xavotirlanmaymiz, chunki quvur ularni avtomatik ravishda boshqaradi.

Oqimlarni amalga oshirish

Node.js-dagi oqimlar haqida gapirganda, ikkita asosiy farq bor:

  • Oqimlarni amalga oshirish vazifasi.
  • Ularni iste'mol qilish vazifasi.

Hozircha biz faqat iste'mol oqimlari haqida gaplashdik. Qani, amalga oshiraylik!

Oqim ijrochilari odatda oqim modulini talab qiladiganlardir.

Yozib olinadigan oqimni amalga oshirish

Yozib olinadigan oqimni amalga oshirish uchun biz oqim modulidan Writing konstruktoridan foydalanishimiz kerak.

const {Yoziladigan} = talab qilish ('stream');

Biz yoziladigan oqimni ko'p jihatdan amalga oshirishimiz mumkin. Agar xohlasak, masalan, Writing konstruktorini kengaytirishimiz mumkin

sinf myWritableStream Writing kengaytiradi {
}

Biroq, men sodda konstruktor yondashuvini afzal ko'raman. Biz faqat Writing konstruktoridan ob'ekt yaratamiz va unga bir qator imkoniyatlarni beramiz. Faqatgina talab qilinadigan variant - bu yozish funktsiyalarini blokirovka qiladigan yozish funktsiyasi.

const {Yoziladigan} = talab qilish ('stream');
const outStream = yangi Yoziladigan ({
  yozish (cheklash, kodlash, qayta qo'ng'iroq) {
    konsol.log (chunk.toString ());
    qayta qo'ng'iroq qilish();
  }
});

process.stdin.pipe (outStream);

Ushbu yozish usuli uchta dalilni oladi.

  • Oqimni boshqacha sozlamasak, korpus odatda tampon bo'ladi.
  • Kodlash dalili bu holatda kerak, ammo odatda biz buni e'tiborsiz qoldiramiz.
  • Qo'ng'iroqni qaytarish - bu ma'lumotlar uzatilishini qayta ishlashni tugatgandan so'ng, biz qo'ng'iroq qilishimiz kerak bo'lgan funktsiya. Yozish muvaffaqiyatli bo'lgan yoki qilmaganligi signalidir. Nosozlik haqida signal berish uchun, xato ob'ekti bilan qayta qo'ng'iroq qiling.

OutStream-da biz shunchaki konsol.log-ni sim sifatida yig'amiz va qayta qo'ng'iroqni amalga oshiramiz, shunda muvaffaqiyatni aniqlay olamiz. Bu juda sodda va ehtimol unchalik foydali bo'lmagan aks sado. U olgan narsasining aksini qaytaradi.

Ushbu oqimni ishlatish uchun biz uni process.stdin yordamida, ya'ni o'qilishi mumkin bo'lgan oqim bilan ishlatishimiz mumkin, shuning uchun process.stdin-ni bizning tashqi kanalimizga yuborishimiz mumkin.

Yuqoridagi kodni ishga tushirganimizda, process.stdin-ga kiritgan har birimiz outStream console.log liniyasi yordamida qaytariladi.

Amalga oshirish uchun bu juda foydali oqim emas, chunki u allaqachon amalga oshirilgan va o'rnatilgan. Bu process.stdout-ga juda mos keladi. Faqat stdni stdoutga ulashimiz mumkin va biz ushbu bitta chiziq bilan aynan bir xil echo xususiyatini olamiz:

process.stdin.pipe (process.stdout);

O'qiladigan oqimni amalga oshiring

O'qilishi mumkin bo'lgan oqimni amalga oshirish uchun biz o'qiladigan interfeysni talab qilamiz va undan ob'ektni quramiz va oqim sozlamalari parametrida read () usulini qo'llaymiz:

const {O'qilishi mumkin} = talab qilish ('stream');
const inStream = yangi o'qiladigan ({
  o'qish () {}
});

O'qiladigan oqimlarni amalga oshirishning oddiy usuli mavjud. Iste'molchilar istagan ma'lumotlarni to'g'ridan-to'g'ri oldirishimiz mumkin.

const {O'qilishi mumkin} = talab qilish ('stream');
const inStream = yangi o'qiladigan ({
  o'qish () {}
});
inStream.push ('ABCDEFGHIJKLM');
inStream.push ('NOPQRSTUVWXYZ');
inStream.push (null); // Boshqa ma'lumotlar yo'q
inStream.pipe (process.stdout);

Nol ob'ektni bosganimizda, biz oqim boshqa ma'lumot yo'qligini bildirishni xohlaymiz.

Ushbu oddiy o'qiladigan oqimni iste'mol qilish uchun biz uni yozib olinadigan process.stdout oqimiga ulashimiz mumkin.

Yuqoridagi kodni ishga tushirganda, biz inStream-dagi barcha ma'lumotlarni o'qiymiz va odatiy holga qaytaramiz. Juda sodda, ammo ayni paytda unchalik samarali emas.

Oqimdagi barcha ma'lumotlarni uni process.stdout-ga yuborishdan oldin siqib chiqarmoqdamiz. Eng yaxshi usul - iste'molchi talab qilgan paytda ma'lumotni talab qilish. Buni biz konfiguratsiya ob'ektida read () usulini qo'llash orqali amalga oshirishimiz mumkin:

const inStream = yangi o'qiladigan ({
  o'qish (hajmi) {
    // ma'lumotlarga talab mavjud ... Kimdir uni o'qishni xohlaydi.
  }
});

O'qish usuli o'qiladigan oqimga chaqirilganda, amalga oshirish qisman ma'lumotni navbatga o'tkazishi mumkin. Masalan, biz bitta harfni 65 kod belgisidan boshlab (A belgisini bildiradi) va har bir bosishda kattalashtirishimiz mumkin:

const inStream = yangi o'qiladigan ({
  o'qish (hajmi) {
    this.push (String.fromCharCode (this.currentCharCode ++));
    if (this.currentCharCode> 90) {
      this.push (null);
    }
  }
});
inStream.currentCharCode = 65;
inStream.pipe (process.stdout);

Iste'molchi o'qiladigan oqimni o'qiyotganda, o'qish usuli davom etadi va biz ko'proq harflarni bosamiz. Biz bu tsiklni biron bir joyda to'xtatishimiz kerak va shuning uchun agar mavjud CharCode 90 dan katta bo'lsa (u Z ni ifodalasa) nolga aylantiriladigan gap.

Ushbu kod biz boshlagan sodda kodga teng, ammo hozirda biz iste'molchi talab qilgan paytda ma'lumotni talab qilmoqdamiz. Siz buni doimo qilishingiz kerak.

Dupleks / Transformatsiya oqimlarini amalga oshirish

Duplex oqimlari bilan biz bir xil ob'ekt yordamida ikkala o'qiladigan va yoziladigan oqimlarni amalga oshirishimiz mumkin. Go'yo biz ikkala interfeysni meros qilib olamiz.

Mana yuqorida yozilgan va o'qilishi mumkin bo'lgan ikkita misolni birlashtirgan dublyaj oqimi:

const {Duplex} = talab qilish ('stream');

const inoutStream = yangi Dupleks ({
  yozish (cheklash, kodlash, qayta qo'ng'iroq) {
    konsol.log (chunk.toString ());
    qayta qo'ng'iroq qilish();
  },

  o'qish (hajmi) {
    this.push (String.fromCharCode (this.currentCharCode ++));
    if (this.currentCharCode> 90) {
      this.push (null);
    }
  }
});

inoutStream.currentCharCode = 65;
process.stdin.pipe (inoutStream) .pipe (process.stdout);

Usullarni birlashtirib, biz ushbu ikki tomonlama oqimdan A dan Z gacha bo'lgan harflarni o'qish uchun foydalanishimiz mumkin va biz uni echo xususiyati uchun ham foydalanishimiz mumkin. Biz echo xususiyatidan foydalanish uchun o'qiladigan stdin oqimini ushbu dupleks oqimga o'tkazamiz va A-dan Z gacha bo'lgan harflarni ko'rish uchun dupleks oqimni yoziladigan stdout oqimiga o'tkazamiz.

Ikki tomonlama oqimning o'qilishi va yozilishi mumkin bo'lgan tomonlari bir-biridan mutlaqo mustaqil ravishda ishlashini tushunish muhimdir. Bu shunchaki ikkita xususiyatni ob'ektga guruhlash.

Transformatsiya oqimi yanada qiziqarli ikki tomonlama oqimdir, chunki uning chiqishi uning kirishidan hisoblangan.

Transformatsiya oqimi uchun biz o'qish yoki yozish usullarini qo'llashimiz shart emas, faqat ikkalasini birlashtirgan transformatsiya usulini qo'llashimiz kerak. U yozish usulining imzosiga ega va biz undan ma'lumotlarni yuborish uchun ham foydalanishimiz mumkin.

Mana bu oddiy transformatsiya oqimi, unda siz yozgan narsangizni bosh harf formatiga o'tkazgandan so'ng takrorlaydi:

const {Transform} = talab qilish ('stream');

const üstCaseTr = yangi aylantirish ({
  transform (uzatma, kodlash, qayta terish) {
    this.push (chunk.toString (). toUpperCase ());
    qayta qo'ng'iroq qilish();
  }
});

process.stdin.pipe (yuxarıCaseTr) .pipe (process.stdout);

Oldingi dupleks oqim misolidagi kabi to'liq iste'mol qiladigan ushbu transformatsiya oqimida biz faqat transform () usulini amalga oshirdik. Ushbu usulda biz bo'lakni katta harf versiyasiga aylantiramiz va keyin uni o'qiladigan qism sifatida itaramiz.

Oqimlar ob'ekti rejimi

Odatiy bo'lib, oqim bufer / satr qiymatlarini kutadi. Ob'ektning biron bir ob'ekti qabul qilinishini sozlashimiz mumkin bo'lgan objectMode bayrog'i bor.

Buni namoyish qilish uchun oddiy misol. O'zgartirish oqimlarining quyidagi kombinatsiyasi JavaScript ob'ektiga vergul bilan ajratilgan qiymatlar qatorini xaritalash uchun xususiyat yaratadi. Shunday qilib, "a, b, c, d" {a: b, c: d} bo'ladi.

const {Transform} = talab qilish ('stream');
const commaSplitter = yangi o'zgarish ({
  readableObjectMode: haqiqiy,
  transform (uzatma, kodlash, qayta terish) {
    this.push (chunk.toString (). qirqish (). bo'linish (','));
    qayta qo'ng'iroq qilish();
  }
});
const arrayToObject = yangi Transform ({
  readableObjectMode: haqiqiy,
  writableObjectMode: haqiqiy,
  transform (uzatma, kodlash, qayta terish) {
    const obj = {};
    uchun (bo'lsin i = 0; i 
const objectToString = yangi Transform ({
  writableObjectMode: haqiqiy,
  transform (uzatma, kodlash, qayta terish) {
    this.push (JSON.stringify (chunk) + '\ n');
    qayta qo'ng'iroq qilish();
  }
});
process.stdin
  .pipe (vergulSplitter)
  .pipe (arrayToObject)
  .pipe (objectToString)
  .pipe (process.stdout)

Biz kirish satrini (masalan, "a, b, c, d") massivni o'qiladigan ma'lumotlar sifatida (["a", "b", "c", "d"]) itarib yuboradigan commaSplitter orqali o'tkazamiz. Ushbu oqim ustiga readableObjectMode bayrog'ini qo'shish kerak, chunki biz bu erda ob'ektni satr emas, balki itaramiz.

Keyin biz massivni olamiz va uni arrayToObject oqimiga o'tkazamiz. Ushbu oqim ob'ektni qabul qilishi uchun bizga WritableObjectMode bayrog'i kerak. Bundan tashqari u ob'ektni suradi (kirish massivi ob'ektga biriktirilgan) va shuning uchun bizda shuningdek, readableObjectMode bayrog'i ham kerak edi. Oxirgi ob'ektToString oqimi ob'ektni qabul qiladi, lekin uni chiqaradi va shuning uchun biz u erda faqat writableObjectMode bayrog'ini talab qilamiz. O'qilishi mumkin bo'lgan qism oddiy satr (satrlangan ob'ekt).

Yuqoridagi misoldan foydalanish

Node-ning ichiga o'rnatilgan oqim oqimlari

Tugun bir nechta juda foydali o'rnatilgan transformatsion oqimlarga ega. Aynan zlib va ​​kripto oqimlari.

Faylni siqish skriptini yaratish uchun zlib.createGzip () oqimini fs o'qiladigan / yoziladigan oqimlari bilan birgalikda ishlatadigan misol.

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

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

Siz argument sifatida uzatadigan har qanday faylni gzip qilish uchun ushbu skriptdan foydalanishingiz mumkin. Biz ushbu fayl uchun o'qiladigan oqimni zlib o'rnatilgan transformatsiya oqimiga va so'ngra yangi giplangan fayl uchun yozib olinadigan oqimga ulayapmiz. Oddiy.

Quvurlarni ishlatishning ajoyib tomoni shundaki, agar kerak bo'lsa, biz ularni voqealar bilan birlashtira olamiz. Aytaylik, men foydalanuvchidan skript ishlayotganda taraqqiyot ko'rsatkichini va skript tugaganida "Bajarildi" xabarini ko'rishini istayman. Quvurlar usuli belgilangan oqimni qaytarganligi sababli, biz voqealarni qayta ishlov beruvchilarni ro'yxatga olishni ham zanjirlashimiz mumkin:

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

fs.createReadStream (fayl)
  .pipe (zlib.createGzip ())
  .on ('data', () => process.stdout.write ('.'))
  .pipe (fs.createWriteStream (fayl + '.zz'))
  .on ('tugatish', () => console.log ('Bajarildi'));

Shunday qilib, quvur usuli bilan biz oqimlarni osongina iste'mol qilamiz, ammo biz zarur bo'lganda voqealar yordamida ushbu oqimlar bilan o'zaro munosabatimizni yanada rivojlantirishimiz mumkin.

Quvurlar usuli nimadan foydalidir, ammo biz undan dasturni qismlarga bo'lib tuzish uchun foydalanishimiz mumkin. Masalan, yuqoridagi ma'lumotlar hodisasini tinglashning o'rniga, biz jarayon haqida hisobot berish uchun o'zgaruvchan oqim yaratamiz va .on () qo'ng'iroqni boshqa .pipe () qo'ng'iroqiga almashtiramiz:

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

const {Transform} = talab qilish ('stream');

const reportProgress = yangi o'zgarish ({
  transform (uzatma, kodlash, qayta terish) {
    process.stdout.write ('.');
    qayta qo'ng'iroq qilish (null, bo'sh);
  }
});

fs.createReadStream (fayl)
  .pipe (zlib.createGzip ())
  .pipe (reportProgress)
  .pipe (fs.createWriteStream (fayl + '.zz'))
  .on ('tugatish', () => console.log ('Bajarildi'));

HisobotProgress oqimi - bu oddiy o'tish oqimi, ammo natijalar ham standartlarga mos keladi. Qaytarib yuborish () funktsiyasida ikkinchi argumentni transform () usuli ichidagi ma'lumotlarni surish uchun qanday ishlatganimga e'tibor bering. Bu avval ma'lumotni itarishga tengdir.

Birlashtiruvchi oqimlarni qo'llash cheksizdir. Masalan, agar biz faylni gzip qilishdan oldin yoki undan keyin shifrlashimiz kerak bo'lsa, biz qilishimiz kerak bo'lgan narsa bu boshqa kerakli oqimni kerakli tartibda boshqa quvurga o'tkazish. Buning uchun biz Node-ning kripto modulidan foydalanishimiz mumkin:

const crypto = zarur ('kripto');
// ...
fs.createReadStream (fayl)
  .pipe (zlib.createGzip ())
  .pipe (crypto.createCipher ('aes192', 'a_secret'))
  .pipe (reportProgress)
  .pipe (fs.createWriteStream (fayl + '.zz'))
  .on ('tugatish', () => console.log ('Bajarildi'));

Yuqoridagi skript uzatilgan faylni siqadi va shifrlaydi va chiqarilgan fayldan faqat sirni bilganlar foydalanishi mumkin. Ushbu faylni shifrlanganligi sababli normal ochish vositasi yordamida ocholmaymiz.

Aslida yuqoridagi skript bilan bog'langan narsalarni ochish uchun biz teskari tartibda kripto va zlib uchun qarama-qarshi oqimlardan foydalanishimiz kerak:

fs.createReadStream (fayl)
  .pipe (kripto.createDecipher ('aes192', 'a_secret'))
  .pipe (zlib.createGunzip ())
  .pipe (reportProgress)
  .pipe (fs.createWriteStream (fayl.slice (0, -3)))
  .on ('tugatish', () => console.log ('Bajarildi'));

O'tkazilgan fayl siqilgan versiya deb faraz qilsak, yuqoridagi kod bundan keyin o'qish oqimini yaratadi, uni kripto createDecipher () oqimiga uzatadi (xuddi shu sirdan foydalanib), zlib createGunzip () oqimiga o'tkazadi va keyin fayllarni kengaytma qismisiz qayta yozib oling.

Ushbu mavzu uchun menda bor narsa. O'qiganingiz uchun rahmat! Keyingi safargacha!

Ta'lim reaktsiyasi yoki tugunmi? Mening kitoblarimni tekshiring:

  • Asoslardan tashqari munosabatda bo'ling
  • Asoslardan tashqari tugun