前言

Node.js这一块内容有点多,所以这篇博客可能不会一次性写完,会随着后面的学习慢慢更新……

Node.js简介

Node.js发布于2009年5月,由Ryan Dahl开发,是一个基于Chrome V8引擎的JavaScript运行环境,让JavaScript 得以运行服务端。众所周知,JavaScript也可以在浏览器中运行,如果想了解两者的区别可以通过官网的说明来了解Node.js 和浏览器之间的区别 (p2hp.com)

Node.js的用途

  1. 开发服务端应用:类似Java、Go等语言编写应用后端程序
  2. 开发工具类应用:Webpack,VIte,Babel
  3. 开发桌面端应用:Electron(借助Node.js开发出来的桌面应用程序框架)

Node.js通过使用核心模块和第三方模块来组织代码,下面就对常用的模块来进行学习。

fs模块

在Node.js中,文件系统(file system)模块是与文件和目录交互的核心模块之一。它提供了一系列的方法来读取、写入和管理文件,允许我们在服务器上高效地处理文件。Node.js的fs模块提供了两套API——Promise API和Callback API,下面主要讲的是Callback API,想要了解Promise API的可以前往官网文件系统 |Node.js v19 API — File system | Node.js v19 API (p2hp.com)查看

fs.writeFile(file, data[, options], callback)

异步向文件写入数据。

参数说明

  • file:文件名,异步将数据写入文件,如果文件已存在,则替换该文件,若不存在则创建文件
  • data:写入文件的数据,可以为string | Buffer | TypedArray | DataView
  • options:配置对象,可配置encoding、mode、flag(‘w’,’a’等)
  • callback:回调函数,有一个err参数,如果成功写入err为null
1
2
3
4
5
6
7
8
9
const fs = require('fs')

fs.writeFile('./fs-file.txt', 'fs module\n', err => {
if (err) {
console.log(err)
return
}
console.log('fs.writeFile success!')
})

fs.writeFileSync(file, data[, options])

同步向文件写入数据。

参数说明

  • file:文件名,异步将数据写入文件,如果文件已存在,则替换该文件,若不存在则创建文件
  • data:写入文件的数据,可以为string | Buffer | TypedArray | DataView
  • options:配置对象,可配置encoding、mode、flag(‘w’,’a’等)
1
fs.writeFileSync('./fs-file.txt', 'fs writeFileSync\n', {flag: 'a'})

options对象里面的flag为’a’表示在文件末尾追加。

fs.appendFile(path, data[, options], callback)

异步追加文件内容,参数和fs.writeFile一样。

1
2
3
4
5
6
7
fs.appendFile('./fs-file.txt', 'fs appendFile', err => {
if (err) {
console.log(err)
return
}
console.log('fs.appendFile success!')
})

fs.appendFileSync(path, data[, options])

同步追加文件内容,参数和异步方法api类型大致一样,只是变成同步方法,少了回调函数。

fs.createWriteStream(path[, options])

流式写入api,适用于大文件写入或者频繁写入的场景。

参数说明

  • path:文件路径
  • options:配置对象(写入模式、编码等等)

返回值

fs.WriteStream对象:

  • write方法写入;
  • end方法是当可写流中没有更多的数据需要写入时,会触发end事件。这通常意味着所有的数据都已经成功写入,并且流已经准备好进行后续处理或关闭;
  • close方法是在文件或流关闭时触发的。它不仅仅是因为没有更多的数据需要写入而触发,而是因为流或文件已经被关闭;
1
2
3
4
5
6
7
8
9
10
11
const ws = fs.createWriteStream('./fs-file.txt', {flags: 'a'})

ws.write('file-createWriteStream\n')

ws.close(err => {
if (err) {
console.log(err)
return
}
console.log('success')
})

fs.readFile(path[, options], callback)

异步读取文件。

参数说明

  • path:文件路径
  • options:配置对象
  • callback:回调函数,有err和data两个参数,如果读取成功err为null且读取的数据存放在data中为Buffer类型,如果读取失败则err不为null
1
2
3
4
5
6
7
fs.readFile('./fs-file.txt', (err, data) => {
if (err) {
console.log(err)
return
}
console.log(data.toString())
})

fs.readFileSync(path[, options])

同步读取文件,参数相比于异步方法少了回调函数,对于读取失败可以通过try-catch捕获。

参数说明

  • path:文件路径
  • options:配置对象

返回值

返回文件内容(Buffer类型)

1
2
3
4
5
6
try{
const data = fs.readFileSync('./fs-file.txt')
console.log(data.toString())
}catch (e) {
console.log(e)
}

fs.createReadStream(path[, options])

流式读取api,适用于大文件读取的场景。

返回值

fs.ReadStream对象:

  • 绑定data事件,每次从文件读取一块数据就触发回调函数,并把读取内容传给形参(一块数据 === 65536字节/64KB)
  • 绑定end事件,文件读取完成后触发回调函数
1
2
3
4
5
6
7
8
9
10
const rs = fs.createReadStream('./music/music.flac')
const ws = fs.createWriteStream('./music/music-copy.flac')

rs.on('data', chunk => {
ws.write(chunk)
})

rs.on('end', () => {
console.log('Read End!')
})

fs.rename(oldPath, newPath, callback)

将 oldPath 中的文件异步重命名为作为 newPath 提供的路径名,如果newPath和oldPath目录层级不一样,则会移动文件,因此可以利用此api实现文件重命名和移动。

参数说明

  • oldPath:旧文件路径,具体到文件名
  • newPath:新文件路径,具体到文件名
  • callback:回调函数,只有err一个参数
1
2
3
4
5
6
7
fs.rename('./music/music.flac','./music.flac',err => {
if(err){
console.log(err)
return
}
console.log('success')
})

此方法是异步方法,同样有对应的同步方法:fs.renameSync(oldPath,newPath)

异步删除文件或符号链接。除了可能的异常之外,不会为完成回调提供任何参数。

参数说明

  • path:文件路径
  • callback:回调函数
1
2
3
4
5
6
7
fs.unlink('./music.flac',err => {
if(err){
console.log(err)
return
}
console.log('delete success')
})

此方法是异步方法,同样有对应的同步方法:fs.unlinkSync(path)

fs.rm(path[, options], callback)

异步删除文件和目录。除了可能的异常之外,不会为完成回调提供任何参数。与unlink不同的是,rm可以用来删除文件夹,并且提供了多种配置选项。

参数说明

  • path:文件路径或者文件夹路径
  • options:配置对象
    • force:如果为true,如果 path 不存在,则将忽略异常。默认值:false。
    • maxRetries:如果遇到 EBUSY、EMFILE、ENFILE、ENOTEMPTY 或 EPERM 错误,Node.js将重试该操作,每次尝试时retryDelay毫秒的线性回退等待。此选项表示重试次数。如果递归选项不为 true,则忽略此选项。默认值:0。
    • recursive:递归选项,如果为true,则执行递归删除。在递归模式下,如果失败,将重试操作。
    • retryDelay:两次重试之间等待的时间量(以毫秒为单位)。
  • callback:回调函数,参数为err,成功删除则err为null
1
2
3
4
5
6
7
fs.rm('./music', {recursive: true}, err => {
if (err) {
console.log(err)
return
}
console.log('delete success')
})

同步方法:fs.rmdirSync(path[, options])

fs.mkdir(path[, options], callback)

异步创建目录,也可递归创建目录(递归创建需要options的recursive为true)

参数说明

  • path:要创建的目录
  • options:配置对象
    • recursive:是否递归创建目录,默认为false
    • mode:在Windows下不支持该参数
  • callback:回调函数,有err和path两个参数,仅当recursive为true时才有path参数,值为递归创建的文件夹路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 非递归创建
fs.mkdir('./a', err => {
if (err) {
console.log(err)
return
}
console.log('success')
})
// 递归创建
fs.mkdir('./a/b/c', {recursive: true}, (err, path) => {
if(err){
console.log(err)
return
}
console.log(path)
})

同步方法:fs.mkdirSync(path[, options])

fs.readdir(path[, options], callback)

读取目录的内容。回调获取两个参数(err、files),其中 files是目录中文件名称的数组.

参数说明

  • path:文件夹路径
  • options:配置对象(encoding、withFileTypes)
  • callback:回调函数
1
2
3
4
5
6
7
fs.readdir('./',{withFileTypes:true},(err, files) => {
if (err){
console.log(err)
return
}
console.log(files)
})

同步方法:fs.readdirSync(path[, options]),返回目录下文件列表

fs.stat(path[, options], callback)

获取对应path的文件/文件夹资源信息。

参数说明

  • path:文件/文件夹路径
  • options:配置对象
  • callback:回调函数,err和stats两个参数,err为null时stats表示对应文件/文件夹的资源信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
fs.stat('./fs-file.txt',(err, stats) => {
if(err){
console.log(err)
return
}
console.log(stats)
})
// Stats {
// dev: 648906302,
// mode: 33206,
// nlink: 1,
// uid: 0,
// gid: 0,
// rdev: 0,
// blksize: 4096,
// ino: 4503599627670657,
// size: 82,
// blocks: 0,
// atimeMs: 1723879750451.2693,
// mtimeMs: 1723878851838.4558,
// ctimeMs: 1723878851838.4558,
// birthtimeMs: 1723865499733.0498,
// atime: 2024-08-17T07:29:10.451Z, // 最后访问时间
// mtime: 2024-08-17T07:14:11.838Z, // 最后修改时间
// ctime: 2024-08-17T07:14:11.838Z, // 状态改变时间
// birthtime: 2024-08-17T03:31:39.733Z // 文件创建时间
// }

path模块

path 模块提供了用于处理文件和目录路径的实用方法,下表是主要常用的一些方法:

API 功能
path.resolve 拼接规范的绝对路径
path.sep 获取当前操作系统的路径分隔符
path.parse 解析绝对路径并返回对象
path.basename 获取路径的基础名称——路径最内层名称
ath.dirname 获取路径的目录名——除去路径最内层名称
path.extname 获取路径的扩展名

对于path模块的使用还要认识以下两个特殊变量:

  • __dirname:表示当前执行脚本所在的目录的绝对路径
  • __filename:表示当前执行脚本的绝对路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const path = require('path')

console.log(__dirname) // D:\desk\Frontend\hand\Node.js
console.log(__filename) // D:\desk\Frontend\hand\Node.js\path.js

let p1 = path.resolve(__dirname, 'index.html')
console.log(p1) // D:\desk\Frontend\hand\Node.js\index.html

let sep = path.sep
console.log(sep) // \

let obj = path.parse(p1)
console.log(obj)

// {
// root: 'D:\\',
// dir: 'D:\\desk\\Frontend\\hand\\Node.js',
// base: 'index.html',
// ext: '.html',
// name: 'index'
// }

let basename = path.basename(p1)
console.log(basename) // index.html

let dir = path.dirname(p1)
console.log(dir) // D:\desk\Frontend\hand\Node.js

let ext = path.extname(p1)
console.log(ext) // .html

http模块

http 模块是 Node.js 官方提供的、用来创建 web 服务器的模块。通过 http 模块提供的http.createServer() 方法,就能方便的把一台普通的电脑,变成一台 Web 服务器,从而对外提供 Web 资源服务。下面演示了http创建web服务器的常用方法。

创建web服务器

  1. 引入http模块
  2. http.createServer()创建Server对象
  3. Server对象监听对应端口的请求
1
2
3
4
5
6
7
8
9
const http = require('http');

const server = http.createServer((request, response) => {
response.end('NodeJS http Module');
})

server.listen(9000, () => {
console.log('server start at : http://127.0.0.1:9000/')
})

通过上述代码我们创建了一个Web服务器用以监听9000端口的请求,一旦我们访问http://127.0.0.1:9000/就会向这个端口发起http请求,进而触发server的回调,web服务器就会返回字符串`NodeJS http Module`。

获取请求报文内容

http模块也为我们提供了一些api用来获取请求的相关信息,一些通用的api如下表所示:

API 功能
request.method 获取请求的方法:GET/POST/PUT/DELETE等
request.url 获取请求的路径
request.httpVersion 获取http协议的版本号
request.headers 获取http请求的请求头

对于POST请求还有请求体,可以通过绑定request的data事件来获取请求体的内容。下面是获取http请求的相关信息api的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
const http = require('http');

const server = http.createServer((request, response) => {
// 获取请求的方法
console.log(request.method)
// 获取请求的url
console.log(request.url)
// 获取http协议的版本号
console.log(request.httpVersion)
// 获取http请求的请求头headers
console.log(request.headers.host)

// 获取请求体
let reqBody = ''
request.on('data', chunk => {
reqBody += chunk
})
request.on('end',()=>{
console.log(reqBody)
response.end('NodeJS HTTP!')
})
})

server.listen(9000, () => {
console.log('server start at : http://127.0.0.1:9000/')
})

请求路径参数解析

对于GET方法的http请求可以在url会携带一些参数,如果想要提取出来,node为我们提供了两种方式,一种是通过node内置的url模块,另一种是通过创建URL对象的方式,前一种方式因为官网说未来可能会弃用,因此下面以第二种方式为例来解析请求路径参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const http = require('http');

const server = http.createServer((request, response) => {
let base = 'http://127.0.0.1'
let _url = new URL(request.url, base) // 传入相对路径和根路径
console.log(_url)
console.log(_url.searchParams.get('keyword')) // 通过searchParams的get方法获取路径参数

response.end('NodeJS HTTP!')
})

server.listen(9000, () => {
console.log('server start at : http://127.0.0.1:9000/')
})

打印的URL对象如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
URL {
href: 'http://127.0.0.1/search?keyword=h5',
origin: 'http://127.0.0.1',
protocol: 'http:',
username: '',
password: '',
host: '127.0.0.1',
hostname: '127.0.0.1',
port: '',
pathname: '/search',
search: '?keyword=h5',
searchParams: URLSearchParams { 'keyword' => 'h5' }, // 路径参数,通过get方法获取
hash: ''
}

设置响应报文内容

获取请求报文之后,就要对请求做出响应,下表是设置请求报文的相关API。

API 功能
response.statusCode 设置响应状态码
response.statusMessage 设置响应信息
response.setHeader 设置响应头
response.write 传入字符串设置响应体信息,可以有多次调用
response.end 在response.write结束后调用,同样可传信息,但是只能调用一次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const http = require('http');

const server = http.createServer((request, response) => {
// 设置响应状态码
response.statusCode = 200 // 默认200
// 设置响应信息
response.statusMessage = 'Response Success!'
// 设置响应头
response.setHeader('Server', 'Node.js')
response.setHeader('test', ['test-01', 'test-02']) // 设置同名响应头
// 设置响应体
response.write('app')
response.end('NodeJS HTTP!')
})

server.listen(9000, () => {
console.log('server start at : http://127.0.0.1:9000/')
})

通过以上步骤可以制作一个简单的web服务器,但是对于一些庞大的后端项目来说就有点捉襟见肘了,后面会专门出一片博客来讲基于Node.js的Web开发应用框架——Express。