Module Module
In Node.js, module loading typically involves three steps: path analysis, file location, and compilation execution.
Module loading prioritizes the following order:
- System Cache: After a module is executed, it is cached. The system first checks the cache for the module.
- System Modules: Native modules have the next highest priority after the cache. Some core modules are compiled into binaries, skipping path analysis and file location, and are loaded directly into memory. System modules are defined in the
libdirectory of the Node.js source code. - File Modules: Modules starting with
.,.., or/are loaded first. If no file extension is provided, Node.js attempts to append.js,.json, and.nodeextensions in that order. For performance optimization, it’s recommended to include the file extension for.jsonand.nodefiles, as Node.js uses synchronous blocking to check file existence. - Directory as Module: If a file module is not found but a directory is, Node.js treats the directory as a package per CommonJS specifications. It looks for a
package.jsonfile in the project root and uses themainfield (e.g.,"main": "lib/hello.js") to locate the entry file. If not found, it throws an error:Error: Cannot find module 'lib/hello.js'. - node_modules Directory: If neither system nor file modules are found, Node.js searches the
node_modulesdirectory in the parent directory, continuing up to the system root.
const exports = module.exports;
Node Module Loading Order
For relative modules, the current script’s path serves as the base path. For example, in a script a.ts with let x = require("./b"), TypeScript searches in this order:
- Check if
b.ts,b.tsx, orb.d.tsexists in the current directory. - Check if a subdirectory
bexists, and if it contains apackage.jsonfile with atypesfield specifying an entry file. If so, load that file. - Check if subdirectory
bcontainsindex.ts,index.tsx, orindex.d.ts.
For non-relative modules, Node.js starts from the current script’s path and searches upward for a node_modules directory. For example, in a.js with let x = require("b"), TypeScript searches in this order:
- Check if
b.ts,b.tsx, orb.d.tsexists in the current directory’snode_modules. - Check if
node_modulescontains apackage.jsonfile with atypesfield specifying an entry file. If so, load that file. - Check if
node_modulescontains an@typessubdirectory withb.d.ts. - Check if
node_modulescontains a subdirectorybwithindex.ts,index.tsx, orindex.d.ts. - Move to the parent directory and repeat the above steps until found.
module.exports and exports
Execution
(function (exports, require, module, __filename, __dirname) { // Wrapper head
console.log('hello world!') // Original file
}); // Wrapper tailexports
exportsis a property ofmodule, defaulting to an empty object.- Requiring a module returns its
exportsproperty. exports.xxxexports an object with multiple properties.module.exports = xxxexports a single object.
Usage
// module-2.js
exports.method = function () {
return 'Hello';
};
exports.method2 = function () {
return 'Hello again';
};
// module-1.js
const module2 = require('./module-2');
console.log(module2.method()); // Hello
console.log(module2.method2()); // Hello againEvents Module
The Events module (EventEmitter) is a critical component in Node.js, implementing the publish/subscribe pattern. It is foundational to many Node.js modules, such as Net, HTTP, FS, and Stream.
const EventEmitter = require('events').EventEmitter;
const emitter = new EventEmitter();
// Register a listener
emitter.on('wake-up', function (time) {
console.log(`Starting at ${time} in the morning, keep pushing forward!`);
});
// Trigger the event
emitter.emit('wake-up', '6:00');EventEmitter in the Stream Module
const EventEmitter = require('events');
const util = require('util');
function Stream() {
EventEmitter.call(this);
}
util.inherits(Stream, EventEmitter);EventEmitter in the Net Module
const EventEmitter = require('events');
const util = require('util');
function Server(options, connectionListener) {
if (!(this instanceof Server))
return new Server(options, connectionListener);
EventEmitter.call(this);
// ...
}
util.inherits(Server, EventEmitter);Using EventEmitter to Handle Avalanche Issues in High Concurrency
The once method ensures a listener is executed only once and then removed. By queuing callbacks for identical requests in the event queue, you can prevent redundant queries for the same file or database, reducing overhead.
const events = require('events');
const emitter = new events.EventEmitter();
const fs = require('fs');
const status = {};
const select = function (file, filename, cb) {
emitter.once(file, cb);
if (status[file] === undefined) {
status[file] = 'ready'; // Set default value
}
if (status[file] === 'ready') {
status[file] = 'pending';
fs.readFile(file, function (err, result) {
console.log(filename);
emitter.emit(file, err, result.toString());
status[file] = 'ready';
setTimeout(function () {
delete status[file];
}, 1000);
});
}
};
for (let i = 1; i <= 11; i++) {
if (i % 2 === 0) {
select(`/tmp/a.txt`, 'File a', function (err, result) {
console.log('err: ', err, 'result: ', result);
});
} else {
select(`/tmp/b.txt`, 'File b', function (err, result) {
console.log('err: ', err, 'result: ', result);
});
}
}Despite multiple file query requests, the fs module only executes two queries (for files a and b). The once listener prevents redundant queries for identical requests, addressing concurrency issues.
Always register an error event listener to handle errors:
const events = require('events');
const emitter = new events.EventEmitter();
emitter.on('error', function (err) {
console.error(err);
});
emitter.emit('error', new Error('This is an error'));
console.log('test');Crypto Module
The Crypto module, built on C/C++ with OpenSSL, provides JavaScript interfaces for hashing, HMAC, encryption, decryption, signing, and verification.
Data Encryption
crypto.createCipher(algorithm, pwd): Creates a cipher object with the specified algorithm and password.crypto.createCipheriv(algorithm, pwd, iv): Creates a cipher object with the specified algorithm, password, and initialization vector.
function cipher(str) {
try {
const crypto = require('crypto');
const cipher = crypto.createCipheriv('des-ecb', '12345678', '');
/**
* update method
* 1st param: Data to encrypt
* 2nd param: Input data format ('utf8', 'ascii', 'latin1')
* 3rd param: Output format ('latin1', 'base64', 'hex'). Returns Buffer if unspecified
*/
let encrypted = cipher.update(str, 'utf8', 'hex');
/**
* final method: Returns remaining encrypted content
* Param: Output format ('latin1', 'base64', 'hex'). Returns Buffer if unspecified
*/
encrypted += cipher.final('hex');
return encrypted;
} catch (e) {
console.log('Encryption failed');
return e.message || e;
}
}
cipher('hello world !!!'); // 81c66a1d39d302205c55f0afac95c06bc985155d4ddb751cData Decryption
crypto.createDecipher(algorithm, pwd): Creates a decipher object with the specified algorithm and password.crypto.createDecipheriv(algorithm, pwd, iv): Creates a decipher object with the specified algorithm, password, and initialization vector.
function decipher(encrypted) {
try {
const crypto = require('crypto');
const decipher = crypto.createDecipheriv('des-ecb', '12345678', '');
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
} catch (e) {
console.log('Decryption failed');
return e.message || e;
}
}
decipher('81c66a1d39d302205c55f0afac95c06bc985155d4ddb751c'); // hello world !!!sha1, md5, sha256, sha512 Encryption
const crypto = require('crypto');
const md5 = str => {
return crypto.createHash('md5').update(str, 'utf8').digest('hex');
};
const sha256 = str => {
return crypto.createHash('sha256').update(str, 'utf8').digest('hex');
};
const sha512 = str => {
return crypto.createHash('sha512').update(str, 'utf8').digest('hex');
};
// Default output: 32-bit lowercase
// 25f9e794323b453885f5181f1b624d0b
console.log(md5('123456789'));
// Convert to 32-bit uppercase
// 25F9E794323B453885F5181F1B624D0B
console.log(sha256('123456789').toUpperCase());
console.log(sha512('123456789').toUpperCase());Buffer Module
The Buffer module is used to read or manipulate binary data streams.
Creating a Buffer (Three Methods)
const b1 = Buffer.from('10', 'utf8');
const bAlloc1 = Buffer.alloc(10); // Creates a 10-byte buffer
const bAllocUnsafe1 = Buffer.allocUnsafe(10);Buffer Character Encoding
// Supported encodings: 'ascii', 'utf8', 'utf16le', 'base64', 'ucs2', 'latin1', 'binary', 'hex'
const buf = Buffer.from('hello world', 'ascii');
console.log(buf.toString('hex')); // 68656c6c6f20776f726c64Converting Between Strings and Buffers
const buf = Buffer.from('Node.js Tech Stack', 'utf-8'); // String to Buffer
const str = buf.toString('utf-8', 0, 9); // Buffer to StringBuffer Memory Mechanism
Buffers are allocated in JavaScript using out-of-heap memory:
- Upon initialization, an 8KB memory space is allocated (as seen in
buffer.jssource code). - Memory requests are classified as small or large Buffer objects.
- For small Buffers:
- If the slab space is sufficient, it uses the remaining space and updates the allocation state (increasing the offset).
- If insufficient, a new slab is created for allocation.
- For large Buffers, the
createUnsafeBuffer(size)function is used directly. - Memory allocation occurs at the C++ level, while management is handled in JavaScript, allowing V8’s garbage collector to reclaim memory.
Buffer vs. Cache
- Buffer: Temporary storage for binary stream data, used to accumulate data before processing (e.g., video players buffer stream data before saving to disk).
- Cache: A persistent intermediate layer for storing frequently accessed data to improve access speed (e.g., using Memory or Redis to cache data from disks or third-party APIs).
Console Logging Module
The node:console module provides a simple debugging console similar to the JavaScript console in web browsers. The Console class includes methods like console.log(), console.error(), and console.warn().
Logger Module Implementation
- Initialize a Logger object.
- Validate parameters to ensure the object is a Logger instance and a writable stream.
- Define
_stdout,_stderr, and other properties for the Logger. - Bind prototype methods to the Logger instance.
- Implement
log,error,warn,trace,clear, and other methods.
const util = require('util');
/**
* Initialize Logger object
* @param {*} stdout
* @param {*} stderr
*/
function Logger(stdout, stderr) {
// Step 1: Check if the current object is a Logger instance
if (!(this instanceof Logger)) {
return new Logger(stdout, stderr);
}
// Check if stdout is a writable stream
if (!stdout || !(stdout.write instanceof Function)) {
throw new Error('Logger expects a writable stream instance');
}
// Use stdout as stderr if not specified
if (!stderr) {
stderr = stdout;
}
// Define object properties
const props = {
writable: true, // Can the property be modified? Default: true
enumerable: false, // Can the property be enumerated in for-in loops? Default: true
configurable: false // Can the property be deleted or redefined? Default: true
};
// Define _stdout property
Object.defineProperty(this, '_stdout', Object.assign(props, {
value: stdout,
}));
// Define _stderr property
Object.defineProperty(this, '_stderr', Object.assign(props, {
value: stderr,
}));
// Define _times property
Object.defineProperty(this, '_times', Object.assign(props, {
value: new Map(),
}));
// Bind prototype methods to the Logger instance
const keys = Object.keys(Logger.prototype);
for (let k in keys) {
this[keys[k]] = this[keys[k]].bind(this);
}
}
// Define log method
Logger.prototype.log = function () {
this._stdout.write(util.format.apply(this, arguments) + '\n');
};
Logger.prototype.info = Logger.prototype.log;
// Define warn method
Logger.prototype.warn = function () {
this._stderr.write(util.format.apply(this, arguments) + `\n`);
};
Logger.prototype.error = Logger.prototype.warn;
// Return current call stack information
Logger.prototype.trace = function trace(...args) {
const err = {
name: 'Trace',
message: util.format.apply(null, args)
};
// V8 Stack Trace API: https://github.com/v8/v8/wiki/Stack-Trace-API
Error.captureStackTrace(err, trace);
this.error(err.stack);
};
// Clear console information
Logger.prototype.clear = function () {
if (this._stdout.isTTY) {
const { cursorTo, clearScreenDown } = require('readline');
cursorTo(this._stdout, 0, 0); // Move cursor to specified position
clearScreenDown(this._stdout); // Clear from cursor position downward
}
};
// Output an object directly
Logger.prototype.dir = function (object, options) {
options = Object.assign({ customInspect: false }, options);
/**
* util.inspect(object, [showHidden], [depth], [colors]) converts objects to strings for debugging.
* showHidden: If true, includes hidden properties.
* depth: Specifies max recursion depth. Default: 3. Null for unlimited.
* colors: If true, outputs with ANSI color coding for better terminal display.
*/
this._stdout.write(util.inspect(object, options) + '\n');
};
// Start timer
Logger.prototype.time = function (label) {
// process.hrtime() returns high-resolution time as [seconds, nanoseconds]
this._times.set(label, process.hrtime());
};
// End timer
Logger.prototype.timeEnd = function (label) {
const time = this._times.get(label);
if (!time) {
process.emitWarning(`No such label '${label}' for console.timeEnd()`);
return;
}
const duration = process.hrtime(time);
const ms = duration[0] * 1000 + duration[1] / 1e6; // 1e6 = 1,000,000
this.log('%s: %sms', label, ms.toFixed(3));
this._times.delete(label);
};
module.exports = new Logger(process.stdout, process.stderr);
module.exports.Logger = Logger;Net Network Module
Network Models
- OSI Seven-Layer Model: Application, Presentation, Session, Transport, Network, Data Link, Physical.
- TCP/IP Five-Layer Model: Application, Transport, Network, Data Link, Physical.
TCP Protocol
Three characteristics: Connection-oriented, Byte stream-oriented, Reliable.
Building a TCP Service
// server.js
const net = require('net');
const server = net.createServer(function (socket) {
// New connection
socket.on('data', function (data) {
socket.write('Hello');
});
socket.on('end', function () {
console.log('Connection closed');
});
socket.write('Welcome to the "Node.js In-Depth" example:\n');
});
server.listen(8124, function () {
console.log('Server bound');
});
// client.js
const net = require('net');
const client = net.connect({ port: 8124 }, function () {
console.log('Client connected');
client.write('world!\r\n');
});
client.on('data', function (data) {
console.log(data.toString());
client.end();
});
client.on('end', function () {
console.log('Client disconnected');
});Building a UDP Service
// server.js
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', function (msg, rinfo) {
console.log(`Server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});
server.on('listening', function () {
const address = server.address();
console.log(`Server listening ${address.address}:${address.port}`);
});
server.bind(41234);
// client.js
const dgram = require('dgram');
const message = Buffer.from('Node.js In-Depth');
const client = dgram.createSocket('udp4');
client.send(message, 0, message.length, 41234, 'localhost', function (err, bytes) {
client.close();
});
// Output
// $ node server.js
// server listening 0.0.0.0:41234
// server got: Node.js In-Depth from 127.0.0.1:58682Building an HTTP Service
const http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');Building a WebSocket Service
const http = require('http');
const crypto = require('crypto');
function WebSocket(url) {
// Pseudo-code: Parse ws://127.0.0.1:12010/updates for request
this.options = parseUrl(url);
this.connect();
}
WebSocket.prototype.onopen = function () {
// TODO
};
WebSocket.prototype.setSocket = function (socket) {
this.socket = socket;
};
WebSocket.prototype.connect = function () {
const that = this;
const key = Buffer.from(this.options.protocolVersion + '-' + Date.now()).toString('base64');
const shasum = crypto.createHash('sha1');
const expected = shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64');
const options = {
port: this.options.port, // 12010
host: this.options.hostname, // 127.0.0.1
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket',
'Sec-WebSocket-Version': this.options.protocolVersion,
'Sec-WebSocket-Key': key
}
};
const req = http.request(options);
req.end();
req.on('upgrade', function (res, socket, upgradeHead) {
// Connection successful
that.setSocket(socket);
that.onopen();
});
};
// Server-side response
const server = http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
});
server.listen(12010);
server.on('upgrade', function (req, socket, upgradeHead) {
const head = Buffer.alloc(upgradeHead.length);
upgradeHead.copy(head);
const key = req.headers['sec-websocket-key'];
const shasum = crypto.createHash('sha1');
const acceptKey = shasum.update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64');
const headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: ' + acceptKey,
'Sec-WebSocket-Protocol: ' + protocol
];
socket.setNoDelay(true);
socket.write(headers.concat('', '').join('\r\n'));
const websocket = new WebSocket();
websocket.setSocket(socket);
});File System (Fs) Module
The Node.js File System (fs) module provides both asynchronous and synchronous methods. For example, reading file contents can be done with asynchronous fs.readFile() or synchronous fs.readFileSync().
POSIX File System
| Method | Description |
|---|---|
| fs.truncate | Truncates or extends a file to a specified length |
| fs.ftruncate | Like truncate, but takes a file descriptor as a parameter |
| fs.chown | Changes the owner and group of a file |
| fs.fchown | Like chown, but takes a file descriptor as a parameter |
| fs.lchown | Like chown, but does not resolve symbolic links |
| fs.stat | Retrieves file status |
| fs.lstat | Like stat, but returns info about symbolic links |
| fs.fstat | Like stat, but takes a file descriptor as a parameter |
| fs.link | Creates a hard link |
| fs.symlink | Creates a symbolic link |
| fs.readlink | Reads the value of a symbolic link |
| fs.realpath | Returns the canonicalized absolute pathname |
| fs.unlink | Deletes a file |
| fs.rmdir | Deletes a directory |
| fs.mkdir | Creates a directory |
| fs.readdir | Reads the contents of a directory |
| fs.close | Closes a file descriptor |
| fs.open | Opens or creates a file for reading or writing |
| fs.utimes | Sets file access and modification times |
| fs.futimes | Like utimes, but takes a file descriptor as a parameter |
| fs.fsync | Synchronizes file data to disk |
| fs.write | Writes data to a file |
| fs.read | Reads data from a file |
Open File
fs.open(path, flags[, mode], callback)Get File Information
const fs = require('fs');
fs.stat('/Users/liuht/code/itbilu/demo/fs.js', function (err, stats) {
console.log(stats.isFile()); // true
});Write File
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(filename, data[, options])Read File
fs.read(fd, buffer, offset, length, position, callback)
fs.readFile(filename[, options], callback)
fs.readFileSync(filename[, options])Close File
fs.close(fd, callback)Delete File
fs.unlink(path, callback)Create Directory
fs.mkdir(path[, options], callback)Read Directory
fs.readdir(path, callback)Delete Directory
fs.rmdir(path, callback)const fs = require('fs');
const buf = Buffer.alloc(1024);
console.log('Preparing to open an existing file!');
fs.open('input.txt', 'r+', function (err, fd) {
if (err) {
return console.error(err);
}
console.log('File opened successfully!');
console.log('Preparing to read file:');
fs.read(fd, buf, 0, buf.length, 0, function (err, bytes) {
if (err) {
console.log(err);
}
console.log(bytes + ' bytes read');
if (bytes > 0) {
console.log(buf.slice(0, bytes).toString());
}
});
});Directory Traversal with fs.readdir and fs.stat
Use fs.readdir to read files and subdirectories in a directory, and fs.stat to check if each item is a file or directory. Here’s a recursive example:
const fs = require('fs');
const path = require('path');
function traverseDirectory(directory, callback) {
fs.readdir(directory, (err, files) => {
if (err) {
callback(err);
return;
}
let remainingFiles = files.length;
if (remainingFiles === 0) {
callback(null);
return;
}
files.forEach(file => {
fs.stat(path.join(directory, file), (err, stat) => {
if (err) {
callback(err);
return;
}
if (stat.isDirectory()) {
traverseDirectory(path.join(directory, file), callback);
} else {
console.log(file);
}
if (--remainingFiles === 0) {
callback(null);
}
});
});
});
}
traverseDirectory('./some-directory', (err) => {
if (err) {
console.error('Error traversing directory:', err);
} else {
console.log('Directory traversal completed.');
}
});Asynchronous Traversal with fs.promises
The fs.promises API provides Promise-based file system operations for easier asynchronous handling.
const fs = require('fs').promises;
const path = require('path');
async function traverseDirectory(directory) {
try {
const files = await fs.readdir(directory);
for (const file of files) {
const filePath = path.join(directory, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
await traverseDirectory(filePath);
} else {
console.log(file);
}
}
} catch (err) {
console.error('Error traversing directory:', err);
}
}
traverseDirectory('./some-directory')
.then(() => console.log('Directory traversal completed.'));Synchronous Traversal
Synchronous methods are not recommended for production but can be useful for quick prototyping or debugging.
const fs = require('fs');
const path = require('path');
function traverseDirectorySync(directory) {
const files = fs.readdirSync(directory);
for (const file of files) {
const filePath = path.join(directory, file);
const stats = fs.lstatSync(filePath);
if (stats.isDirectory()) {
traverseDirectorySync(filePath);
} else {
console.log(file);
}
}
}
try {
traverseDirectorySync('./some-directory');
console.log('Directory traversal completed.');
} catch (err) {
console.error('Error traversing directory:', err);
}HTTP Module
// 1. Import the http module
const http = require('http');
// 2. Create a web server instance
const server = http.createServer();
// 3. Start the server
server.listen(3000, () => {
console.log('My server started');
});
// 4. Bind the request event to handle client requests
server.on('request', () => {
console.log('Hello HTML');
});Path Module
const path = require('path');
// Normalize path
console.log('normalization: ' + path.normalize('/test/test1//2slashes/1slash/tab/..'));
// Join paths
console.log('joint path: ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));
// Resolve to absolute path
console.log('resolve: ' + path.resolve('main.js'));
// Get file extension
console.log('ext name: ' + path.extname('main.js'));URL Module
The URL module provides three methods: url.parse(), url.format(), and url.resolve() for parsing URLs.
const url = require('url');
const adr = 'http://localhost:8888/index.html?year=2019&month=September';
const q = url.parse(adr, true);
console.log(q.host);
console.log(q.pathname);
console.log(q.search);
const qdata = q.query;
console.log(qdata.month);Util Module
The Util module provides utility functions, including: util.callbackify, util.inherits, util.inspect, util.isArray(object), util.isRegExp(object), and util.isDate(object).
OS Module
const os = require('os');
// CPU byte order
console.log('endianness: ' + os.endianness());
// Operating system name
console.log('type: ' + os.type());
// Operating system platform
console.log('platform: ' + os.platform());
// Total system memory
console.log('total memory: ' + os.totalmem() + ' bytes.');
// Free system memory
console.log('free memory: ' + os.freemem() + ' bytes.');Domain Module
const EventEmitter = require('events').EventEmitter;
const domain = require('domain');
const emitter1 = new EventEmitter();
// Create domain
const domain1 = domain.create();
domain1.on('error', function (err) {
console.log('domain1 handles this error (' + err.message + ')');
});
// Explicit binding
domain1.add(emitter1);
emitter1.on('error', function (err) {
console.log('Listener handles this error (' + err.message + ')');
});
emitter1.emit('error', new Error('Handled by listener'));
emitter1.removeAllListeners('error');
emitter1.emit('error', new Error('Handled by domain1'));
const domain2 = domain.create();
domain2.on('error', function (err) {
console.log('domain2 handles this error (' + err.message + ')');
});
// Implicit binding
domain2.run(function () {
const emitter2 = new EventEmitter();
emitter2.emit('error', new Error('Handled by domain2'));
});
domain1.remove(emitter1);
emitter1.emit('error', new Error('Converted to exception, system will crash!'));DNS Module
const dns = require('dns');
dns.lookup('www.github.com', function onLookup(err, address, family) {
console.log('IP address:', address);
dns.reverse(address, function (err, hostnames) {
if (err) {
console.log(err.stack);
}
console.log('Reverse lookup ' + address + ': ' + JSON.stringify(hostnames));
});
});Stream Module
Streams come in four types:
- Readable: For read operations.
- Writable: For write operations.
- Duplex: For both read and write operations.
- Transform: For writing data and reading transformed results.
Reading from a Stream
const fs = require('fs');
let data = '';
// Create readable stream
const readerStream = fs.createReadStream('input.txt');
// Set encoding to utf8
readerStream.setEncoding('UTF8');
// Handle stream events: data, end, error
readerStream.on('data', function (chunk) {
data += chunk;
});
readerStream.on('end', function () {
console.log(data);
});
readerStream.on('error', function (err) {
console.log(err.stack);
});
console.log('Program execution completed');Writing to a Stream
const fs = require('fs');
const data = 'Rookie Tutorial Website: www.runoob.com';
// Create a writable stream to output.txt
const writerStream = fs.createWriteStream('output.txt');
// Write data with utf8 encoding
writerStream.write(data, 'UTF8');
// Mark end of file
writerStream.end();
// Handle stream events: finish, error
writerStream.on('finish', function () {
console.log('Write completed.');
});
writerStream.on('error', function (err) {
console.log(err.stack);
});
console.log('Program execution completed');Piping Streams
const fs = require('fs');
// Create readable stream
const readerStream = fs.createReadStream('input.txt');
// Create writable stream
const writerStream = fs.createWriteStream('output.txt');
// Pipe read to write: Read input.txt and write to output.txt
readerStream.pipe(writerStream);
console.log('Program execution completed');Chaining Streams
const fs = require('fs');
const zlib = require('zlib');
// Compress input.txt to input.txt.gz
fs.createReadStream('input.txt')
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream('input.txt.gz'));
console.log('File compression completed.');



