理解 Node.js 中的 HTTP 服务器和文件流
在本文中,我们将探讨如何使用 Node.js 创建一个简单的 HTTP 服务器,并使用文件系统模块(fs
)来处理文件数据。我们会通过一个具体的例子来说明同步读取和异步流的概念,以及如何将这些数据通过 HTTP 响应发送给客户端。
环境搭建
首先,确保你的开发环境中安装了 Node.js。你可以从 Node.js 官网 下载并安装它。
前言,本次例子将会用到
fs
、http
、path
模块,以及http.createServer()
、fs.readFileSync()
、path.join()
、fs.createReadStream()
、pipe()
相关api,本文会做相关介绍,如需详细学习可查阅相关文档
1. 创建 HTTP 服务器
我们使用 Node.js 的内置 http
模块来创建服务器。以下是创建基本服务器的步骤:
- 引入
http
模块。 - 创建服务器实例,使用
http.createServer
方法。 - 监听请求并响应。
const http = require('http'); const server = http.createServer((req, res) => { // 处理请求的逻辑 }); server.listen(3000, '127.0.0.1', () => { console.log('Server is listening on port 3000'); });
http.createServer((req, res) => { // 处理请求的逻辑 });
这个函数通常有两个参数,req
和res
,分别代表请求和响应对象。
请求对象 reqreq.url:
请求的 URL。(常用 路由)req.method:
请求的方法(如 GET 或 POST)。(常用)req.headers:
请求头对象。(常用)req.httpVersion:
HTTP 协议版本。req.setEncoding():
设置请求体的编码,默认为 UTF-8。req.on('data', callback):
当请求体数据到达时调用的回调函数。req.on('end', callback):
当请求体完全接收完毕时调用的回调函数。
响应对象 resres.statusCode:
响应的 HTTP 状态码,默认为 200。(不常用)res.statusMessage:
响应的 HTTP 状态信息。res.headersSent:
一个布尔值,指示头部是否已经被发送。(不常用)res.setHeader(name, value):
设置响应头。(不常用)res.getHeader(name):
获取响应头的值。(不常用)res.removeHeader(name):
移除响应头。(不常用)res.writeHead(statusCode[, statusMessage][, headers]):
发送响应头。(常用 复合属性,状态码+状态信息+响应头)res.write(chunk[, encoding][, callback]):
发送响应体的一部分。(流式传输)res.end([data][, encoding][, callback]):
结束响应过程,发送响应体的剩余部分,并关闭连接。(流式传输)
同步读取文件
Node.js 的 fs
模块提供了同步和异步的文件操作方法。fs.readFileSync
是一个同步方法,它会阻塞事件循环直到文件读取完成。
const fs = require('fs'); const path = require('path'); let dataPath = path.join(__dirname, 'data.txt'); let data = fs.readFileSync(dataPath, 'utf8');
path
模块提供了用于处理和转换文件路径的工具。它是一个内置模块,无需额外安装。path.join([...paths]):
这个方法用于将多个路径片段合并成一个完整的路径。
它会考虑操作系统的路径分隔符 (例如,在 Windows 上是反斜杠 \,在 Unix/Linux/Mac 上是正斜杠 /),所以如果直接用字符串拼接(__dirname+‘/data.txt’)可能会出现正反斜杠混用的情况fs.readFileSync
函数接收两个或三个参数:文件路径(path):
这是第一个参数,表示要读取的文件的路径。可以是一个字符串,也可以是一个 Buffer 或 URL 对象。编码(encoding):
这是第二个参数,是一个可选的字符串,用来指定文件内容的字符编码。如果你提供了这个参数,函数返回的将是字符串而不是 Buffer。常见的编码包括 ‘utf8’、‘ascii’、‘latin1’ 等。回调函数(callback):
这是第三个参数,也是一个可选的函数,用于异步操作完成后的回调(fs.readFile异步会用到)。虽然 fs.readFileSync 是同步函数,但 Node.js 文档中提到了这个参数,可能是为了与异步版本的 fs.readFile 保持一致性。在 fs.readFileSync 中,这个回调参数通常不被使用,因为数据会立即返回。
异步读取文件
与同步方法不同,fs.createReadStream
创建一个可读流,允许你以非阻塞方式读取文件。
let userInfoPath = path.join(__dirname, 'userInfo.json'); let userInfoStream = fs.createReadStream(userInfoPath, 'utf8');
使用管道传输数据
pipe
方法用于将一个流的数据自动发送到另一个流。在这个例子中,我们将 userInfo.json
的内容通过管道发送给 HTTP 响应对象。
userInfoStream.pipe(res, { end: true });
发送响应
最后,我们使用 res.write
和 res.end
方法发送响应给客户端。
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); //writeHead这里写了状态码200和响应头{ 'Content-Type': 'text/html; charset=utf-8' } res.write('<h1>Hello Node!</h1>' + data); res.end();
完整代码示例
以下是将上述知识点整合到一起的完整代码示例:
const http = require('http'); const fs = require('fs'); const path = require('path'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); // 同步读取 data.txt 文件内容 let data = fs.readFileSync(path.join(__dirname, 'data.txt'), 'utf8'); // 异步读取 userInfo.json 文件内容 let userInfoPath = path.join(__dirname, 'userInfo.json'); let userInfoStream = fs.createReadStream(userInfoPath, 'utf8'); // 将 userInfo.json 的内容通过管道发送给客户端 userInfoStream.pipe(res, { end: true }); // 发送额外的文本内容 res.write('<h1>Hello Node!</h1>' + data + '<br/><br/><br/><br/>'); }); server.listen(3000, '127.0.0.1', () => { console.log('Server is listening on port 3000'); });
结果实例: