不急着写下第一行代码,而是先梳理一下就基本功能而言有哪些步骤。html
建立一个nodejs-static-webserver
目录,在目录内运行npm init
初始化一个package.json文件。node
mkdir nodejs-static-webserver && cd "$_" // initialize package.json npm init
接着建立以下文件目录:web
-- config ---- default.json -- static-server.js -- app.js
default.jsonnpm
{ "port": 9527, "root": "/Users/sheila1227/Public", "indexPage": "index.html" }
default.js
存放一些默认配置,好比端口号、静态文件目录(root)、默认页(indexPage)等。当这样的一个请求http://localhost:9527/myfiles/
抵达时. 若是根据root
映射后获得的目录内有index.html,根据咱们的默认配置,就会给客户端发回index.html的内容。json
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.jsapp
const StaticServer = require('./static-server'); (new StaticServer()).start();
在这个文件内,调用上面的static-server
模块,并建立一个StaticServer实例,调用其start
方法,启动了一个静态资源服务器。这个文件后面将不须要作其余修改,全部对静态资源服务器的完善都发生在static-server.js
内。less
在目录下启动程序会看到成功启动的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()
方法会被调用。