node 静态伺服(搭建服务)
基本功能
不急着写下第一行代码,而是先梳理一下就基本功能而言有哪些步骤。
- 在本地根据指定端口启动一个http server,等待着来自客户端的请求
- 当请求抵达时,根据请求的url,以设置的静态文件目录为base,映射得到文件位置
- 检查文件是否存在
- 如果文件不存在,返回404状态码,发送not found页面到客户端
- 如果文件存在:
- 打开文件待读取
- 设置response header
- 发送文件到客户端
- 等待来自客户端的下一个请求
实现基本功能
代码结构
创建一个nodejs-static-webserver目录,在目录内运行npm init初始化一个package.json文件。
mkdir nodejs-static-webserver && cd "$_" // initialize package.json npm init
接着创建如下文件目录:
-- config
---- default.json
-- static-server.js
-- app.js
default.json
{
"port": 9527,
"root": "/Users/sheila1227/Public",
"indexPage": "index.html"
}
default.js存放一些默认配置,比如端口号、静态文件目录(root)、默认页(indexPage)等。当这样的一个请求http://localhost:9527/myfiles/抵达时. 如果根据root映射后得到的目录内有index.html,根据我们的默认配置,就会给客户端发回index.html的内容。
static-server.js
const http = require('http');
const path = require('path');
const config = require('./config/default');
class StaticServer {
constructor() {
this.port = config.port;
this.root = config.root;
this.indexPage = config.indexPage;
}
start() {
http.createServer((req, res) => {
const pathName = path.join(this.root, path.normalize(req.url));
res.writeHead(200);
res.end(`Requeste path: ${pathName}`);
}).listen(this.port, err => {
if (err) {
console.error(err);
console.info('Failed to start server');
} else {
console.info(`Server started on port ${this.port}`);
}
});
}
}
module.exports = StaticServer;
在这个模块文件内,我们声明了一个StaticServer类,并给其定义了start方法,在该方法体内,创建了一个server对象,监听rquest事件,并将服务器绑定到配置文件指定的端口。在这个阶段,我们对于任何请求都暂时不作区分地简单地返回请求的文件路径。path模块用来规范化连接和解析路径,这样我们就不用特意来处理操作系统间的差异。
app.js
const StaticServer = require('./static-server');
(new StaticServer()).start();
在这个文件内,调用上面的static-server模块,并创建一个StaticServer实例,调用其start方法,启动了一个静态资源服务器。这个文件后面将不需要做其他修改,所有对静态资源服务器的完善都发生在static-server.js内。
在目录下启动程序会看到成功启动的log:
> node app.js
Server started on port 9527
在浏览器中访问,可以看到服务器将请求路径直接返回了。

路由处理
之前我们对任何请求都只是向客户端返回文件位置而已,现在我们将其替换成返回真正的文件:
routeHandler(pathName, req, res) {
}
start() {
http.createServer((req, res) => {
const pathName = path.join(this.root, path.normalize(req.url));
this.routeHandler(pathName, req, res);
}).listen(this.port, err => {
...
});
}
将由routeHandler来处理文件发送。
读取静态文件
读取文件之前,用fs.stat检测文件是否存在,如果文件不存在,回调函数会接收到错误,发送404响应。
respondNotFound(req, res) {
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.end(`<h1>Not Found</h1><p>The requested URL ${req.url} was not found on this server.</p>`);
}
respondFile(pathName, req, res) {
const readStream = fs.createReadStream(pathName);
readStream.pipe(res);
}
routeHandler(pathName, req, res) {
fs.stat(pathName, (err, stat) => {
if (!err) {
this.respondFile(pathName, req, res);
} else {
this.respondNotFound(req, res);
}
});
}
Note:
读取文件,这里用的是流的形式
createReadStream而不是readFile,是因为后者会在得到完整文件内容之前将其先读到内存里。这样万一文件很大,再遇上多个请求同时访问,readFile就承受不来了。使用文件可读流,服务端不用等到数据完全加载到内存再发回给客户端,而是一边读一边发送分块响应。这时响应里会包含如下响应头:Transfer-Encoding:chunked默认情况下,可读流结束时,可写流的
end()方法会被调用。

浙公网安备 33010602011771号