Node.js-ում հոսքերի օգտագործման ներածություն
Node.js-ի հոսքերը կարող են բարդ լինել, բայց արժե ժամանակ տրամադրել դրանք հասկանալու համար:
Հիմնական Takeaways
- Node.js-ի հոսքերը հիմնարար գործիք են տվյալների մշակման և փոխանցման համար՝ դրանք դարձնելով իդեալական իրական ժամանակի և իրադարձությունների վրա հիմնված հավելվածների համար:
- Node.js-ում գրելի հոսք ստեղծելու համար կարող եք օգտագործել fs մոդուլի createWriteStream() ֆունկցիան, որը տվյալներ է գրում կոնկրետ վայրում։
- Ընթեռնելի, գրավոր, դուպլեքս և փոխակերպվող հոսքերի չորս տեսակներն են Node.js-ում, որոնցից յուրաքանչյուրն ունի իր օգտագործման դեպքն ու ֆունկցիոնալությունը:
Հոսքը հիմնարար ծրագրավորման գործիք է, որը զբաղվում է տվյալների հոսքով: Իր հիմքում հոսքը սովորաբար ներկայացնում է բայթերի հաջորդական փոխանցումը մի կետից մյուսը: Node.js-ի պաշտոնական փաստաթղթերը հոսքը սահմանում են որպես վերացական ինտերֆեյս, որը կարող եք օգտագործել տվյալների հետ աշխատելու համար:
Համակարգչով կամ ցանցով տվյալների փոխանցումը հոսքի իդեալական օգտագործում է:
Հոսում է Node.js-ում
Stream-ները էական դեր են խաղացել Node.js-ի հաջողության մեջ: Դրանք իդեալական են իրական ժամանակի տվյալների մշակման և իրադարձությունների վրա հիմնված հավելվածների համար՝ Node.js գործարկման միջավայրի երկու նշանավոր առանձնահատկությունները:
Node.js-ում նոր հոսք ստեղծելու համար դուք պետք է օգտագործեք հոսքի API-ն, որն աշխատում է բացառապես Strings-ի և Node.js-ի բուֆերային տվյալների հետ: Node.js-ն ունի չորս տեսակի հոսքեր՝ գրավոր, ընթեռնելի, դուպլեքս և փոխակերպում։
Ինչպես ստեղծել և օգտագործել գրավոր հոսք
Գրավոր հոսքը թույլ է տալիս գրել կամ ուղարկել տվյալներ որոշակի վայր: fs (ֆայլային համակարգ) մոդուլն ունի WriteStream դաս, որը կարող եք օգտագործել fs.createWriteStream() ֆունկցիայով նոր հոսք ստեղծելու համար։ Այս ֆունկցիան ընդունում է այն ֆայլի ուղին, որի վրա ցանկանում եք գրել տվյալներ, ինչպես նաև ընտրանքների կամընտիր զանգված:
const {createWriteStream} = require("fs");
(() => {
const file = "myFile.txt";
const myWriteStream = createWriteStream(file);
let x = 0;
const writeNumber = 10000;
const writeData = () => {
while (x < writeNumber) {
const chunk = Buffer.from(`${x}, `, "utf-8");
if (x === writeNumber - 1) return myWriteStream.end(chunk);
if (!myWriteStream.write(chunk)) break;
x++
}
};
writeData();
})();
Այս կոդը ներմուծում է createWriteStream() ֆունկցիան, որն այնուհետև օգտագործում է անանուն սլաքի ֆունկցիան՝ ստեղծելու հոսք, որը տվյալներ է գրում myFile.txt-ում: Անանուն ֆունկցիան պարունակում է ներքին ֆունկցիա, որը կոչվում էwriteData(), որը գրում է տվյալներ։
createWriteStream() ֆունկցիան աշխատում է բուֆերի հետ՝ նպատակակետ ֆայլում թվերի հավաքածու գրելու համար (0–9999): Այնուամենայնիվ, երբ գործարկում եք վերը նշված սցենարը, այն նույն գրացուցակում ստեղծում է ֆայլ, որը պարունակում է հետևյալ տվյալները.
Թվերի ներկայիս հավաքածուն ավարտվում է 2915-ով, սակայն այն պետք է ներառեր մինչև 9999 թվեր։ Այս անհամապատասխանությունը տեղի է ունենում, քանի որ յուրաքանչյուր WriteStream օգտագործում է բուֆեր, որը պահում է ֆիքսված քանակությամբ տվյալներ միաժամանակ: Իմանալու համար, թե որն է այս լռելյայն արժեքը, դուք պետք է դիմեք highWaterMark տարբերակը:
console.log("The highWaterMark value is: " +
myWriteStream.writableHighWaterMark + " bytes.");
Անանուն ֆունկցիային վերը նշված կոդի տողը ավելացնելով տերմինալում կստեղծվի հետևյալ ելքը.
Տերմինալի ելքը ցույց է տալիս, որ լռելյայն highWaterMark արժեքը (որը հարմարեցված է) 16384 բայթ է: Սա նշանակում է, որ այս բուֆերում կարող եք միաժամանակ պահել միայն 16384 բայթից ցածր տվյալներ: Այսպիսով, մինչև 2915 թիվը (գումարած բոլոր ստորակետերն ու բացատները) ներկայացնում է տվյալների առավելագույն քանակը, որը բուֆերը կարող է պահել միաժամանակ:
Բուֆերային սխալի լուծումը հոսքային իրադարձություն օգտագործելն է: Հոսքը հանդիպում է տարբեր իրադարձությունների տվյալների փոխանցման գործընթացի տարբեր փուլերում: drain միջոցառումը հարմար տարբերակ է այս իրավիճակի համար:
Վերևի writeData() ֆունկցիայի մեջ զանգը WriteStream-ի write() ֆունկցիային վերադարձնում է true, եթե տվյալների զանգվածը (կամ ներքին բուֆերը) գտնվում է highWaterMark-ից ցածր: արժեքը. Սա ցույց է տալիս, որ հավելվածը կարող է ավելի շատ տվյալներ ուղարկել հոսքին: Այնուամենայնիվ, հենց write() ֆունկցիան վերադարձնում է false, հանգույցը ընդհատվում է, քանի որ դուք պետք է ցամաքեցնել բուֆերը:
myWriteStream.on('drain', () => {
console.log("a drain has occurred...");
writeData();
});
Վերևում drain իրադարձության ծածկագիրը անանուն ֆունկցիայի մեջ տեղադրելու դեպքում WriteStream-ի բուֆերը կդատարկվի, երբ այն իր հզորությամբ լինի: Այնուհետև այն հետ է կանչում writeData() մեթոդը, որպեսզի կարողանա շարունակել տվյալներ գրել: Թարմացված հավելվածի գործարկումը կստեղծի հետևյալ արդյունքը.
Պետք է նկատի ունենալ, որ հավելվածը գործարկման ընթացքում երեք անգամ պետք է ցամաքեցնի WriteStream բուֆերը: Տեքստային ֆայլը նաև որոշակի փոփոխություններ է կրել.
Ինչպես ստեղծել և օգտագործել ընթեռնելի հոսք
Տվյալները կարդալու համար սկսեք ստեղծելով ընթեռնելի հոսք՝ օգտագործելով fs.createReadStream() գործառույթը:
const {createReadStream} = require("fs");
(() => {
const file = "myFile.txt";
const myReadStream = createReadStream(file);
myReadStream.on("open", () => {
console.log(`The read stream has successfully opened ${file}.`);
});
myReadStream.on("data", chunk => {
console.log("The file contains the following data: " + chunk.toString());
});
myReadStream.on("close", () => {
console.log("The file has been successfully closed.");
});
})();
Վերևի սկրիպտը օգտագործում է createReadStream() մեթոդը՝ նախորդ ծածկագրի ստեղծած ֆայլին մուտք գործելու համար՝ myFile.txt: createReadStream() ֆունկցիան ընդունում է ֆայլի ուղին (որը կարող է լինել տողի, բուֆերի կամ URL-ի տեսքով) և մի քանի կամընտիր տարբերակներ՝ որպես արգումենտ։
Անանուն գործառույթում կան մի քանի կարևոր հոսքային իրադարձություններ: Այնուամենայնիվ, ոչ մի նշան չկա «ջրահեռացման» մասին: Դա պայմանավորված է նրանով, որ ընթեռնելի հոսքը բուֆերացնում է տվյալները միայն այն ժամանակ, երբ զանգում եք stream.push(chunk) ֆունկցիան կամ օգտագործում եք ընթեռնելի իրադարձությունը:
բաց իրադարձությունը գործարկվում է, երբ fs-ը բացում է այն ֆայլը, որից ցանկանում եք կարդալ: Երբ դուք կցում եք տվյալներ իրադարձությունը անուղղակի շարունակական հոսքին, դա հանգեցնում է հոսքի անցման հոսքի ռեժիմի: Սա թույլ է տալիս տվյալների փոխանցել դրանք հասանելի լինելուն պես: Վերոնշյալ հավելվածը գործարկելիս ստացվում է հետևյալ արդյունքը.
Ինչպես ստեղծել և օգտագործել դուպլեքս հոսք
Դուպլեքս հոսքն իրականացնում է և՛ գրավոր, և՛ ընթեռնելի հոսքի միջերեսները, այնպես որ կարող եք կարդալ և գրել այդպիսի հոսքի վրա: Օրինակներից մեկը TCP վարդակն է, որն իր ստեղծման համար հիմնված է ցանցային մոդուլի վրա:
Դուպլեքս հոսքի հատկությունները ցուցադրելու պարզ միջոց է ստեղծել TCP սերվեր և հաճախորդ, որը փոխանցում է տվյալներ:
Server.js ֆայլը
const net = require('net');
const port = 5000;
const host = '127.0.0.1';
const server = net.createServer();
server.on('connection', (socket)=> {
console.log('Connection established from client.');
socket.on('data', (data) => {
console.log(data.toString());
});
socket.write("Hi client, I am server " + server.address().address);
socket.on('close', ()=> {
console.log('the socket is closed')
});
});
server.listen(port, host, () => {
console.log('TCP server is running on port: ' + port);
});
The client.js Ֆայլ
const net = require('net');
const client = new net.Socket();
const port = 5000;
const host = '127.0.0.1';
client.connect(port, host, ()=> {
console.log("connected to server!");
client.write("Hi, I'm client " + client.address().address);
});
client.on('data', (data) => {
console.log(data.toString());
client.write("Goodbye");
client.end();
});
client.on('end', () => {
console.log('disconnected from server.');
});
Դուք նկատում եք, որ ինչպես սերվերը, այնպես էլ հաճախորդի սկրիպտները հաղորդակցվելու (տվյալներ փոխանցելու և ստանալու համար) օգտագործում են ընթեռնելի և գրավոր հոսք: Բնականաբար, սերվերի հավելվածը առաջինն է աշխատում և սկսում է լսել կապեր: Հաճախորդը սկսելուն պես այն միանում է սերվերին՝ օգտագործելով TCP պորտի համարը:
Կապ հաստատելուց հետո հաճախորդը սկսում է տվյալների փոխանցումը՝ գրելով սերվերին՝ օգտագործելով իր WriteStream: Սերվերը գրանցում է իր ստացած տվյալները տերմինալում, այնուհետև գրում է տվյալներ՝ օգտագործելով իր WriteStream-ը: Ի վերջո, հաճախորդը գրանցում է իր ստացած տվյալները, գրում է լրացուցիչ տվյալներ, այնուհետև անջատվում է սերվերից: Սերվերը բաց է մնում այլ հաճախորդների համար միանալու համար:
Ինչպես ստեղծել և օգտագործել փոխակերպման հոսք
Փոխակերպման հոսքերը դուպլեքս հոսքեր են, որոնցում ելքը կապված է մուտքի հետ, բայց տարբերվում է դրանցից: Node.js-ն ունի երկու տեսակի Transform հոսքեր՝ zlib և crypto հոսքեր: Zlib հոսքը կարող է սեղմել տեքստային ֆայլը և ապա սեղմել այն ֆայլի փոխանցումից հետո:
CompressFile.js հավելվածը
const zlib = require('zlib');
const { createReadStream, createWriteStream } = require('fs');
(() => {
const source = createReadStream('myFile.txt');
const destination = createWriteStream('myFile.txt.gz');
source.pipe(zlib.createGzip()).pipe(destination);
})();
Այս պարզ սցենարը վերցնում է բնօրինակ տեքստային ֆայլը, սեղմում այն և պահում ընթացիկ գրացուցակում: Սա պարզ գործընթաց է ընթեռնելի հոսքի pipe() մեթոդի շնորհիվ: Հոսքի խողովակաշարերը հեռացնում են բուֆերների և խողովակների տվյալների օգտագործումը անմիջապես մի հոսքից մյուսը:
Այնուամենայնիվ, նախքան տվյալները սկրիպտում գրվող հոսքին հասնելը, մի փոքր շրջանցում է կատարվում zlib-ի createGzip() մեթոդի միջոցով: Այս մեթոդը սեղմում է ֆայլը և վերադարձնում նոր Gzip օբյեկտ, որն այնուհետև ստանում է գրելու հոսքը:
DecompressFile.js հավելվածը
const zlib = require('zlib');
const { createReadStream, createWriteStream } = require('fs');
(() => {
const source = createReadStream('myFile.txt.gz');
const destination = createWriteStream('myFile2.txt');
source.pipe(zlib.createUnzip()).pipe(destination);
})();
Վերևում գտնվող այս սկրիպտը վերցնում է սեղմված ֆայլը և ապասեղմում այն: Եթե բացեք նոր myFile2.txt ֆայլը, կտեսնեք, որ այն պարունակում է նույն տվյալները, ինչ սկզբնական ֆայլը.
Ինչու՞ են հոսքերը կարևոր:
Հոսքերը բարձրացնում են տվյալների փոխանցման արդյունավետությունը: Ընթեռնելի և գրավոր հոսքերը ծառայում են որպես հիմք, որը հնարավորություն է տալիս հաղորդակցվել հաճախորդների և սերվերների միջև, ինչպես նաև սեղմել և փոխանցել մեծ ֆայլեր:
Հոսքերը նաև բարելավում են ծրագրավորման լեզուների աշխատանքը: Առանց հոսքերի տվյալների փոխանցման գործընթացը դառնում է ավելի բարդ՝ պահանջելով ավելի մեծ ձեռքով մուտքագրում մշակողներից և հանգեցնելով ավելի շատ սխալների և կատարողականի խնդիրների: