Node 搭建一个静态资源服务器的实现

使用 Node 的内置模块,创建一个可以访问目录的静态资源服务器,支持fs文件读取,资源压缩与缓存等。

一、创建 HTTP Server 服务器

Node 的 http 模块提供 HTTP 服务器和客户端接口,通过 require('http') 使用。

先创建一个简单的 http server。配置参数如下:

// server/config.js module.exports = { root: process.cwd(), host: '127.0.0.1', port: '8877' }

process.cwd()方法返回 Node.js 进程的当前工作目录,和 Linus 命令 pwd 功能一样,

Node 服务器每次收到 HTTP 请求后都会调用 http.createServer() 这个回调函数,每次收一条请求,都会先解析请求头作为新的 request 的一部分,然后用新的 request 和 respond 对象触发回调函数。以下创建一个简单的 http 服务,先默认响应的 status 为 200:

// server/http.js const http = require('http') const path = require('path') const config = require('./config') const server = http.createServer((request, response) => { let filePath = path.join(config.root, request.url) response.statusCode = 200 response.setHeader('content-type', 'text/html') response.write(`<html><body><h1>Hello World! </h1><p>${filePath}</p></body></html>`) response.end() }) server.listen(config.port, config.host, () => { const addr = `${config.host}:${config.port}` console.info(`server started at ${addr}`) })

客户端请求静态资源的地址可以通过 request.url 获得,然后使用 path 模块拼接资源的路径。

执行 $ node server/http.js 后访问 :8877/ 后的任意地址都会显示该路径:

Node 搭建一个静态资源服务器的实现

每次修改服务器响应内容,都需要重新启动服务器更新,推荐自动监视更新自动重启的插件supervisor,使用supervisor启动服务器。

$ npm install supervisor -D $ supervisor server/http.js

二、使用 fs 读取资源文件

我们的目的是搭建一个静态资源服务器,当访问一个到资源文件或目录时,我们希望可以得到它。这时就需要使用 Node 内置的 fs 模块读取静态资源文件,

使用 fs.stat() 读取文件状态信息,通过回调中的状态 stats.isFile() 判断文件还是目录,并使用 fs.readdir() 读取目录中的文件名

// server/route.js const fs = require('fs') module.exports = function (request, response, filePath){ fs.stat(filePath, (err, stats) => { if (err) { response.statusCode = 404 response.setHeader('content-type', 'text/plain') response.end(`${filePath} is not a file`) return; } if (stats.isFile()) { response.statusCode = 200 response.setHeader('content-type', 'text/plain') fs.createReadStream(filePath).pipe(response) } else if (stats.isDirectory()) { fs.readdir(filePath, (err, files) => { response.statusCode = 200 response.setHeader('content-type', 'text/plain') response.end(files.join(',')) }) } }) }

其中 fs.createReadStream() 读取文件流, pipe() 是分段读取文件到内存,优化高并发的情况。

修改之前的 http server ,引入上面新建的 route.js 作为响应函数:

// server/http.js const http = require('http') const path = require('path') const config = require('./config') const route = require('./route') const server = http.createServer((request, response) => { let filePath = path.join(config.root, request.url) route(request, response, filePath) }) server.listen(config.port, config.host, () => { const addr = `${config.host}:${config.port}` console.info(`server started at ${addr}`) })

再次执行 $ node server/http.js 如果是文件夹则显示目录:

Node 搭建一个静态资源服务器的实现

如果是文件则直接输出:

Node 搭建一个静态资源服务器的实现

成熟的静态资源服务器 anywhere,深入理解 nodejs 作者写的。

三、util.promisify 优化 fs 异步

我们注意到 fs.stat() 和 fs.readdir() 都有 callback 回调。我们结合 Node 的 util.promisify() 来链式操作,代替地狱回调。

util.promisify 只是返回一个 Promise 实例来方便异步操作,并且可以和 async/await 配合使用,修改 route.js 中 fs 操作相关的代码:

// server/route.js const fs = require('fs') const util = require('util') const stat = util.promisify(fs.stat) const readdir = util.promisify(fs.readdir) module.exports = async function (request, response, filePath) { try { const stats = await stat(filePath) if (stats.isFile()) { response.statusCode = 200 response.setHeader('content-type', 'text/plain') fs.createReadStream(filePath).pipe(response) } else if (stats.isDirectory()) { const files = await readdir(filePath) response.statusCode = 200 response.setHeader('content-type', 'text/plain') response.end(files.join(',')) } } catch (err) { console.error(err) response.statusCode = 404 response.setHeader('content-type', 'text/plain') response.end(`${filePath} is not a file`) } }

因为 fs.stat() 和 fs.readdir() 都可能返回 error,所以使用 try-catch 捕获。

使用异步时需注意,异步回调需要使用 await 返回异步操作,不加 await 返回的是一个 promise,而且 await 必须在async里面使用。

四、添加模版引擎

从上面的例子是手工输入文件路径,然后返回资源文件。现在优化这个例子,将文件目录变成 html 的 a 链接,点击后返回文件资源。

在第一个例子中使用 response.write() 插入 HTML 标签,这种方式显然是不友好的。这时候就使用模版引擎做到拼接 HTML。

常用的模版引擎有很多,ejs、jade、handlebars,这里的使用ejs:

npm i ejs

新建一个模版 src/template/index.ejs ,和 html 文件很像:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/7d7e8a9f67e8bf681e062c6461456c48.html