Basic Concepts
Node.js is a JavaScript runtime environment built on Chrome's V8 engine. It utilizes an event-driven, non-blocking I/O model that enables JavaScript to run on the server side.
Official website: https://nodejs.org
Code example:
console.log("hello Node.js");
// 1. Navigate to the directory containing the js file
// 2. Execute node hello.js
// 3. Output: hello Node.js
Sample code repository: https://github.com/yourusername/nodejs-basics
Buffer
Concept
Buffer is an array-like object used to represent fixed-length byte sequences. Essentially, it's a memory space specifically designed for handling binary data.
Characteristics
- Buffer size is fixed and cannot be adjusted
- Buffer offers better performance as it directly operates on computer memory
- Each element occupies 1 byte
Usage
Buffer Creation
There are several ways to create a Buffer in Node.js:
Buffer.alloc
// Creates a 10-byte Buffer, equivalent to allocating 10 bytes of memory space with each byte value set to 0
// Result: <Buffer 00 00 00 00 00 00 00 00 00 00>
const buffer1 = Buffer.alloc(10);
Buffer.allocUnsafe
// Creates a 10-byte Buffer that may contain old data, which could affect execution results, hence the name 'unsafe'
const buffer2 = Buffer.allocUnsafe(10);
Buffer.from
// Create Buffer from string
const buffer3 = Buffer.from('hello');
// Create Buffer from array
const buffer4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
Buffer and String Conversion
Use the toString method to convert Buffer to string:
const buffer = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
// Default encoding is UTF-8
console.log(buffer.toString());
Note: toString uses UTF-8 encoding by default.
Buffer Operations
Buffer can be processed using array notation:
const buffer = Buffer.from('hello');
// Binary: 1101000
console.log(buffer[0].toString(2));
// Modify buffer
buffer[0] = 95;
console.log(buffer.toString());
// Overflow: If the modified value exceeds 255, the excess 8-bit data will be discarded
const buffer = Buffer.from('hello');
// High-order bits will be discarded because the maximum value for 8 bits is 255
buffer[0] = 361;
console.log(buffer);
// Chinese characters: A UTF-8 character typically occupies 3 bytes
const buffer = Buffer.from('你好');
console.log(buffer);
Note: If the modified value exceeds 255, excess bits will be discarded. A UTF-8 character typically occupies 3 bytes.
File System Module
The fs (file system) module is a built-in Node.js module that allows operations on the computer's disk.
File Writing
File writing involves saving data to a file. The following APIs are available:
| Method | Description |
| writeFile | Asynchronous writing |
| writeFileSync | Synchronous writing |
| appendFile/appendFileSync | Append writing |
| createWriteStream | Stream writing |
Asynchronous Writing
Syntax: fs.writeFile(file, data, [options], callback)
Parameters:
- file: filename
- data: data to be written
- options: option settings (optional)
- callback: write callback
Return value: undefined
Code example:
// 1. Import fs module
const fs = require('fs');
// 2. Write to file
// writeFile asynchronously writes with four parameters: 1. file path 2. content 3. configuration 4. callback function
// File write successful
fs.writeFile('./motto.txt', 'Conquer the wolves, meditate at Gu Yan, drink horses at the sea', error => {
// Error is null when writing is successful
if (error) {
console.log('File write failed');
return;
}
console.log('File write successful');
});
Synchronous Writing
Syntax: fs.writeFileSync(file, data, [options])
Parameters:
- file: filename
- data: data to be written
- options: option settings (optional)
Return value: undefined
Code example:
// 1. Import fs module
const fs = require('fs');
// 2. Write to file
// Synchronous write without callback function
fs.writeFileSync('./motto.txt', 'Conquer the wolves, meditate at Gu Yan, drink horses at the sea');
In Node.js, disk operations are performed by other threads. There are two processing modes:
- Synchronous processing: The JavaScript main thread waits for the execution results of other threads before continuing with main thread code, which is less efficient
- Asynchronous processing: The JavaScript main thread doesn't wait for the execution results of other threads and directly continues with subsequent main thread code, which is more efficient
Append Writing
The appendFile function appends content to the end of a file. Its syntax is identical to writeFile.
Syntax:
- fs.appendFile(file, data, [options], callback)
- fs.appendFileSync(file, data, [options])
Return value: Both are undefined
Code example:
// 1. Import fs module
const fs = require('fs');
const content = '\r\nBut if the Flying General of Dragon City is still here, the Hu horses shall not cross the Yin Mountains.';
// fs.appendFile('./motto.txt', content, error => {
// // Error is null when writing is successful
// if (error) {
// console.log('File append write failed');
// return;
// }
// console.log('File append write successful');
// });
// Synchronous file append write
// fs.appendFileSync('./motto.txt', content);
// Use writeFile for append writing
fs.writeFile('./motto.txt', content, {flag: 'a'}, error => {
if (error) {
console.log('File append write failed');
return;
}
console.log('File append write successful');
});
console.log('hello world');
Stream Writing
Syntax: fs.createWriteStream(path, [options])
Parameters:
- path: file path
- options: option configuration (optional)
Return value: Object
Code example:
// 1. Import fs module
const fs = require('fs');
// 2. Create write stream object
const writeStream = fs.createWriteStream('./reading.txt');
// 3. Write content
writeStream.write('A half-acre pond like a mirror opens\r\n');
writeStream.write('Sky and clouds shadows徘徊 together\r\n');
writeStream.write('Ask how the channel gets so clear\r\n');
writeStream.write('Because there is a living source of water\r\n');
// 4. Close the channel (not necessary)
// writeStream.close();
Opening a file consumes resources. Stream writing reduces the number of times files are opened and closed. Stream writing is suitable for large file writing or frequent writing scenarios, while writeFile is suitable for scenarios with low writing frequency.
File Reading
File reading involves retrieving data from files through programs. The following APIs are available:
| Method | Description |
| readFile | Asynchronous reading |
| readFileSync | Synchronous reading |
| createReadStream | Stream reading |
Asynchronous Reading
Syntax: fs.readFile(path, [options], callback)
Parameters:
- path: file path
- options: option configuration
- callback: callback function
Return value: undefined
Code example:
// 1. Import fs module
const fs = require('fs');
// 2. Asynchronous read
fs.readFile('./motto.txt', (error, data) => {
if (error) {
console.log('File read error');
return;
}
console.log(data.toString());
});
Synchronous Reading
Syntax: fs.readFileSync(path, [options])
Parameters:
- path: file path
- options: option configuration
Return value: string | Buffer
Code example:
// 1. Import fs module
const fs = require('fs');
// 2. Synchronous read
let data = fs.readFileSync('./motto.txt');
console.log(data.toString());
Stream Reading
Syntax: fs.createReadStream(path, [options])
Parameters:
- path: file path
- options: option configuration (optional)
Return value: Object
Code example:
// 1. Import fs module
const fs = require('fs');
// 2. Create read stream object
const rs = fs.createReadStream('./reading.txt');
// 3. Bind data event
rs.on('data', chunk => {
// chunk: block, large piece
console.log(chunk);
console.log(chunk.length);
console.log(chunk.toString());
});
// 4. End event (optional)
rs.on('end', () => {
console.log('File reading completed');
});
File Moving and Renaming
In Node.js, you can use rename or renameSync to move or rename files or folders.
Syntax:
- fs.rename(oldPath, newPath, callback)
- fs.renameSync(oldPath, newPath)
Parameters:
- oldPath: current file path
- newPath: new file path
- callback: callback after operation
Code example:
// 1. Import fs module
const fs = require('fs');
// 2. File rename
// fs.rename('./motto-2.txt', './famous-general.txt', error => {
// if (error) {
// console.log('File rename failed');
// return ;
// }
// console.log('File rename successful');
// });
// 3. File move
// Error will be thrown if the folder doesn't exist: Error: ENOENT: no such file or directory
fs.rename('./famous-general.txt', './files/famous-general.txt', error => {
if (error) {
console.log(error, 'Error moving file');
return ;
}
console.log('Operation successful');
});
File Deletion
In Node.js, you can use unlink or unlinkSync or rm or rmSync to delete files.
Syntax:
- fs.unlink(path, callback)
- fs.unlinkSync(path)
Parameters:
- path: file path
- callback: callback after operation
Code example:
// 1. Import fs module
const fs = require('fs');
// 2. Call unlink method
// unlinkSync: synchronous deletion
// fs.unlink('./motto-3.txt', error => {
// if (error) {
// console.log('Delete file error', error)
// return;
// }
// console.log('Delete file successful')
// });
// 3. Call rm method
// rmSync: synchronous deletion
fs.rm('./files/famous-general.txt', error => {
if (error) {
console.log('File deletion failed', error)
return;
}
console.log('File deletion successful')
});
Folder Operations
In Node.js, you can create, read, delete, and perform other operations on folders using the following APIs:
| Method | Description |
| mkdir / mkdirSync | Create folder |
| readdir / readdirSync | Read folder |
| rmdir / rmdirSync | Delete folder |
Create Folder
In Node.js, you can use mkdir or mkdirSync to create folders.
Syntax:
- fs.mkdir(path, [options], callback)
- fs.mkdirSync(path, [options])
Parameters:
- path: folder path
- options: option configuration (optional)
- callback: callback after operation
Example code:
// 1. Import fs module
const fs = require('fs');
// 2. Create folder
// mkdir make: create directory: directory
fs.mkdir('./html', error => {
if (error) {
console.log('Create directory failed', error)
return;
}
console.log('Create directory successful')
});
// 3. Create folder recursively
fs.mkdir('./a/b/c', {
recursive: true
}, error => {
if (error) {
console.log("Create folder recursively failed", error)
return;
}
console.log('Create folder recursively successful')
});
// 4. Create folder recursively synchronously
fs.mkdirSync('./a/b/c', {recursive: true});
Read Folder
In Node.js, you can use readdir or readdirSync to read folders.
Syntax:
- fs.readdir(path, [options], callback)
- fs.readdirSync(path, [options])
Parameters:
- path: folder path
- options: option configuration (optional)
- callback: callback after operation
Example code:
// 1. Import fs module
const fs = require('fs');
// 2. Read folder
// readdir read: read dir:directory directory
fs.readdir('./', (error, data) => {
if (error) {
console.log('Read folder error', error)
return;
}
// [
// '1-file-write.js',
// '2-append-write.js',
// '3-stream-write.js',
// '4-file-read.js',
// '5-stream-read.js',
// '6-exercise-file-copy.js',
// '7-file-rename-move.js',
// '8-delete-file.js',
// '9-folder-operation.js',
// 'a',
// 'html',
// 'motto.txt',
// 'files',
// 'reading.txt'
// ]
console.log(data)
});
// Synchronous read
// let data = fs.readdirSync('./');
// console.log(data);
Delete Folder
In Node.js, you can use rmdir or rmdirSync to delete folders.
Syntax:
- fs.rmdir(path, [options], callback)
- fs.rmdirSync(path, [options])
Parameters:
- path: folder path
- options: option configuration (optional)
- callback: callback after operation
Example code:
// 1. Import fs module
const fs = require('fs');
// 2. Delete folder
// fs.rmdir('./files', error => {
// if (error) {
// console.log('Delete folder failed', error)
// return;
// }
// console.log('Delete folder successful')
// });
// 3. Delete folder recursively
// Recursive delete folder failed [Error: ENOTEMPTY: directory not empty, rmdir 'E:\projects\nodejs-basics\a']
// Not recommended
// fs.rmdir('./a', {recursive: true}, error => {
// if (error) {
// console.log('Recursive delete folder failed', error)
// return ;
// }
// console.log('Recursive delete folder successful')
// });
// Recommended
fs.rm('./a', {recursive: true}, error => {
if (error) {
console.log('Recursive delete file failed', error)
return;
}
console.log('Recursive delete file successful')
});
// Synchronous recursive delete folder
fs.rmdirSync('./a', {recursive: true});
Resource Status Check
In Node.js, you can use stat or statSync to view detailed information about resources.
Syntax:
- fs.stat(path, [options], callback)
- fs.statSync(path, [options])
Parameters:
- path: folder path
- options: option configuration (optional)
- callback: callback after operation
Example code:
// 1. Import fs module
const fs = require('fs');
// 2. stat method: status abbreviation
fs.stat('./reading.txt', (error, data) => {
if (error) {
console.log('Operation failed', error)
return;
}
// Stats {
// dev: 985301708,
// mode: 33206,
// nlink: 1,
// uid: 0,
// gid: 0,
// rdev: 0,
// blksize: 4096,
// ino: 281474979770305,
// size: 92,
// blocks: 0,
// atimeMs: 1684373309132.9426,
// mtimeMs: 1684289136772.1648,
// ctimeMs: 1684289136772.1648,
// birthtimeMs: 1684289136770.7136,
// atime: 2023 - 05 - 18 T01: 28: 29.133 Z,
// mtime: 2023 - 05 - 17 T02: 05: 36.772 Z,
// ctime: 2023 - 05 - 17 T02: 05: 36.772 Z,
// birthtime: 2023 - 05 - 17 T02: 05: 36.771 Z
// }
console.log(data)
console.log(data.isFile())
console.log(data.isDirectory())
});
// 3. Synchronous status retrieval
let data = fs.statSync('./reading.txt');
console.log(data)
console.log(data.isFile())
console.log(data.isDirectory())
Result value object structure:
- size: file size
- birthtime: creation time
- mtime: last modification time
- isFile: check if it's a file
- isDirectory: check if it's a folder
Relative Path Issues
When the fs module operates on resources, there are two ways to write paths:
- Relative path ./motto.txt: motto.txt in the current directory motto.txt: equivalent to the above writing method ../motto.txt: motto.txt in the parent directory of the current directory
- Absolute path D:/Program Files: absolute path on Windows system /usr/bin: absolute path on Linux system
The "current directory" in relative paths refers to the command line's working directory, not the directory where the file is located. Therefore, when the command line's working directory doesn't match the file's directory, some bugs may occur.
__dirname
__dirname is similar to require and is a 'global' variable in the Node.js environment. __dirname stores the absolute path of the current file's directory. You can use __dirname to concatenate with filenames to form absolute paths.
Code example:
let data = fs.readFileSync(__dirname + '/data.txt');
console.log(data);
When using the fs module, try to use __dirname to convert paths to absolute paths to avoid bugs caused by relative paths.
Exercise
Implement file copying functionality.
Code example:
/**
* Requirement:
* Copy [motto.txt]
*/
// 1. Import fs module
const fs = require('fs');
// Global object, an attribute of the global object, accessible without declaration. It's an object that describes the current Node process state, providing a simple interface to the operating system.
const process = require('process')
// Method 1: Use readFile
// 2. Read file
// let data = fs.readFileSync('./motto.txt');
// // 3. Write file
// fs.writeFileSync('./motto-2.txt', data);
// // Check system memory usage
// // rss: 19795968 bytes
// console.log(process.memoryUsage())
// Method 2: Use stream operations
// 2. Create stream read
let readStream = fs.createReadStream('./motto.txt');
// 3. Create stream write
let writeStream = fs.createWriteStream('./motto-3.txt');
// // 4. Bind data event
// readStream.on('data', chunk => {
// writeStream.write(chunk);
// });
// // 5. Bind end event
// readStream.on('end', () => {
// // rss: 20885504 bytes, which uses less memory compared to synchronous approach
// console.log(process.memoryUsage())
// });
// 4. Use pipe to directly copy
readStream.pipe(writeStream);