<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Node Server</title> </head> <body> <% files.forEach(function(name){ %> <a href="../<%= dir %>/<%= name %>" > <%= name %></a><br> <% }) %> </body> </html>
再次修改 route.js,添加 ejs 模版并 ejs.render() ,在文件目录的代码中传递 files、dir 等参数:
// server/route.js const fs = require('fs') const util = require('util') const path = require('path') const ejs = require('ejs') const config = require('./config') // 异步优化 const stat = util.promisify(fs.stat) const readdir = util.promisify(fs.readdir) // 引入模版 const tplPath = path.join(__dirname,'../src/template/index.ejs') const sourse = fs.readFileSync(tplPath) // 读出来的是buffer module.exports = async function (request, response, filePath) { try { const stats = await stat(filePath) if (stats.isFile()) { response.statusCode = 200 ··· } else if (stats.isDirectory()) { const files = await readdir(filePath) response.statusCode = 200 response.setHeader('content-type', 'text/html') // response.end(files.join(',')) const dir = path.relative(config.root, filePath) // 相对于根目录 const data = { files, dir: dir ? `${dir}` : '' // path.relative可能返回空字符串() } const template = ejs.render(sourse.toString(),data) response.end(template) } } catch (err) { response.statusCode = 404 ··· } }
重启动 $ node server/http.js 就可以看到文件目录的链接:
五、匹配文件 MIME 类型
静态资源有图片、css、js、json、html等,
在上面判断 stats.isFile() 后响应头设置的 Content-Type 都为 text/plain,但各种文件有不同的 Mime 类型列表。
我们先根据文件的后缀匹配它的 MIME 类型:
// server/mime.js const path = require('path') const mimeTypes = { 'js': 'application/x-javascript', 'html': 'text/html', 'css': 'text/css', 'txt': "text/plain" } module.exports = (filePath) => { let ext = path.extname(filePath) .split('.').pop().toLowerCase() // 取扩展名 if (!ext) { // 如果没有扩展名,例如是文件 ext = filePath } return mimeTypes[ext] || mimeTypes['txt'] }
匹配到文件的 MIME 类型,再使用 response.setHeader('Content-Type', 'XXX') 设置响应头:
// server/route.js const mime = require('./mime') ··· if (stats.isFile()) { const mimeType = mime(filePath) response.statusCode = 200 response.setHeader('Content-Type', mimeType) fs.createReadStream(filePath).pipe(response) }
运行 server 服务器访问一个文件,可以看到 Content-Type 修改了:
六、文件传输压缩
注意到 request header 中有 Accept—Encoding:gzip,deflate,告诉服务器客户端所支持的压缩方式,响应时 response header 中使用 content-Encoding 标志文件的压缩方式。
node 内置 zlib 模块支持文件压缩。在前面文件读取使用的是 fs.createReadStream() ,所以压缩是对 ReadStream 文件流。示例 gzip,deflate 方式的压缩:
最常用文件压缩,gzip等,使用,对于文件是用ReadStream文件流进行读取的,所以对ReadStream进行压缩:
// server/compress.js const zlib = require('zlib') module.exports = (readStream, request, response) => { const acceptEncoding = request.headers['accept-encoding'] if (!acceptEncoding || !acceptEncoding.match(/\b(gzip|deflate)\b/)) { return readStream } else if (acceptEncoding.match(/\bgzip\b/)) { response.setHeader("Content-Encoding", 'gzip') return readStream.pipe(zlib.createGzip()) } else if (acceptEncoding.match(/\bdeflate\b/)) { response.setHeader("Content-Encoding", 'deflate') return readStream.pipe(zlib.createDeflate()) } }
修改 route.js 文件读取的代码: