手写一个服务器(二)

上一节我们完成的服务起的目录搭建,并写了一个简单的服务,在这一节,将完成函数封装,返回内容…

偷懒神器

每次我么在写玩代码都需要重新启动服务,简直蠢的不要不要的,这里我们使用 supervisor

// supervisor 热更新模块

安装: npm install supervisor -g

使用:supervisor ./src/app.js

supervisor 会自动检测文件改动,然后重启服务

./src/app.js 内容扩展

let config = require('./config')
let path = require('path')
let fs = require('fs')
let mime = require('mime')
let chalk = require('chalk')
let util = require('util')
let url = require('url')
let http = require('http')
let stat = util.promisify(fs.stat)
let ejs = require('ejs')

// debug后面放置的是参数,可以根据后边的参数决定是否打印
// debug 设置环境变量: set DEBUG=XXX
// powershell设置环境变量: $env:DEBUG = "*,-not_this"
let debug = require('debug')('static:app')
let templ = fs.readFileSync(path.join(__dirname, 'tmpl.ejs'), 'utf8')
let readDir = util.promisify(fs.readdir)
// var debug = require('debug');
// console.log(chalk.green('hello'))
// debug('app')

// supervisor 热更新模块
// npm install supervisor 
// 使用: supervisor app.js


// 创建服务
class Server {
    constructor () {
        // 设置配置参数, 列表模板
        this.config = config
        this.templ = templ
    }
    start () {
        let {hostname, port} = this.config
        let server = http.createServer(this.handleRequest())
        let url = `http://${hostname}:${chalk.green(port)}`
        debug(url) // 打印日志
        server.listen(port, hostname)

    }
    // 服务器处理函数
    handleRequest () {
        // 在函数执行的时候需要返回一个函数,不然无法监听事件
        // 这里使用箭头函数可以避免this问题
        return async(req, res) => {
            // 解析路径
            let {pathname} = url.parse(req.url)
            // 拼接完整路径
            let p = path.join(this.config.dir, '.' + pathname)
            // 检测文件是否存在
            try {
                let statObj = await stat(p)

                // 判断 如果当前路径是目录,则展示内容列表,如果是文件直接展示文件内容
                if (statObj.isDirectory()) {
                    // 文件目录
                    // 常用的模板引擎: ejs
                    // let content = ejs.render(this.templ, {dirs: [{path: 'a', name: 'a'}, {path: 'b', name: 'b'}]})
                    let dirs = await readDir(p) // 读取目录的文件路径
                    // 将拿到的目录映射成为一个数组,里边是一个对象{name: xxx, path: xxx}
                    dirs = dirs.map(dir => ({
                        name: dir,
                        path: path.join(pathname, dir)
                    }))
                    // 如果是目录,使用ejs渲染并返回结果
                    let content = ejs.render(this.templ, {dirs})
                    res.end(content)
                } else {
                    // 文件 直接返回
                    // 压缩
                    // 缓存
                    // 断点续传
                   this.sendFile(req, res, p)
                }
            } catch (error) {
                this.sendError(error, req, res)
            }
        }
    }
    // 发送文件
    sendFile (req, res, p) {
        res.setHeader('Content-Type', mime.getType(p) + ';charset=utf8')
        fs.createReadStream(p).pipe(res)
    }
    // 发送错误
    sendError (err, req, res) {
        debug(util.inspect(err).toString())
        res.statusCode = 404
        res.end()
    }
}

let server = new Server()
server.start()

./src/tmpl.ejs

为了方便渲染目录,我们之类引入ejs模板引擎,并在src下创建一个模板

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>server demo</title>
    <link rel="stylesheet" href="/index.css">
</head>
<body>
    <h1>This is dir file list!</h1>
    <ul>
    <% dirs.forEach(dir =>{%>
        <li><a href="<%=dir.path%>"><%=dir.name%></a></li>
    <%})%>
    </ul>
</body>
</html>

总结

到此为止,我们已经可以返回基本的内容,和目录结构的返回,并可以查看对应的内容。

下一步我们将完成压缩,缓存,以及分片上传下载…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值