Skip to content

js
const fs = require('fs').promises
const http = require('http')
const path = require('path')
const mime = require('mime')
const urlParse = require('url')
const { createReadStream } = require('fs')

class StaticServer {
  start (port, cb) {
    // http.createServer() 回调的 this 指向创造的实例,需要将 this 指向 
    // StaticServer 实例
    const server = http.createServer((req, res) => this.handleRequest(req, res))
    
    server.listen(port, () => {
      cb(port)
    })
    server.on('error', err => {
      if (err.code === 'EADDRINUSE') {
        server.listen(++port)
      }
    })
  }

  async handleRequest (req, res) {
    const { pathname } = urlParse.parse(req.url, true)
    // pathname 会出现 /,直接 resolve 结果是根路径下的 pathname
    let filePath = path.join(`${ __dirname }/dist`, pathname)

    try {
      const stat = await fs.stat(filePath)

      if (stat.isFile()) {
        // 内部用一个对象保存所有后缀对应的类型
        res.setHeader('Content-Type', `${ mime.getType(filePath) };charset=utf-8`)
  
        // 可读流.pipe(可写流)
        createReadStream(filePath).pipe(res)
        // const data = await fs.readFile(filePath)
        // res.end(data)
      } else {
        // 文件夹
        filePath = path.join(filePath, 'index.html')
        // 判断文件是否可以访问,是异步的,不存在时报错。同步用 fs.existSync
        await fs.access(filePath)
        res.setHeader('Content-Type', 'text/html;charset=utf-8')
        createReadStream(filePath).pipe(res)
      }
    } catch (e) {
      // 浏览器看心情请求 favicon.ico 图标
      this.sendError(e, res)
    }
    
  }

  sendError (e, res) {
    res.statusCode = 400
    res.end('Not Found')
  }
}

const scheme = 'http',
      host = '127.0.0.1',
      port = 3000

const staticServer = new StaticServer()
staticServer.start(port, (_port) => {
  console.log(`server running: ${ scheme }://${ host }:${ _port }`)
})