【nodejs】手写个静态目录服务并打包

7 篇文章 0 订阅

背景

  • 静态目录服务很多都是有打包好的。但是写代码就怕不想写,写这个文章就是逼着自己不用人家封装好的写一个。

流程

  • 需要第三方模块,mime,chalk,nunjucks,commander。npm安装一下即可。

一、建个bin目录,下面放www文件,编写入口。

#! /usr/bin/env node
let program = require('commander')
let config = {
    "-p,--port <val>":"set http-server port",
    "-d,--dir <dir>":"set http-server directory"
}
Object.entries(config).forEach(([key,value])=>{
    program.option(key,value)
})
program.name('ye-static').usage("<options>")

program.on("--help",()=>{
    console.log('Examples:');
    console.log('$ye-static --port 3000');
})

let obj =program.parse(process.argv)
let Server=require('../static-server')
let defaultConfig = {
    port:3000,
    dir:process.cwd(),
    ...obj
}
let  server = new Server(defaultConfig)
server.start()
  • 首先把命令行做出来,添加个–help例子,解析用户输入的端口号和目录
  • 配置package.json,加入Bin,这样就可以全局调用了。
{
  "name": "staticserver",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "chalk": "^3.0.0",
    "commander": "^4.0.1",
    "mime": "^2.4.4",
    "nunjucks": "^3.2.0"
  },
  "bin": {
    "ye-static": "./bin/www"
  }
}
  • 然后把server类写出个start方法就可以调用了。
class Server{
    constructor(config){
        this.port = config.port
        this.dir = config.dir
    }
    start(){
        console.log('xzcxz');
        console.log(this.port,this.dir);
    }
}

module.exports =Server
  • 在命令行使用npm link 变成全局包,然后使用配置的bin命令进行调试。
  • 如果能运行文件就可以下一步。
  • 下一步需要写让http服务运行起来,只要在start里加上下面2句即可
      let server = http.createServer()
        server.listen(this.port)
  • 然后需要做静态服务,就要监听访问,可以在http.createServer里传入函数,即可监听访问。
 let server = http.createServer(this.handleRequest.bind(this))
  • 这个handleRequest函数也是写在Server下,跟start同级,这样也方便拿port和dir。
  • 下面就是写静态服务,思路是这样:分为是文件还是是目录,是目录就列出孩子然后拿模板渲染出来,是文件就通过流返回给页面。同时需要注意下文件路径和文件类型,文件类型调用mime写好的来看。
  • req.url通过url.parse解码出的pathname是相对于网站根路径的,用来操作子url。
  • abspath就是电脑上的绝对路径,fs通过绝对路径查是目录还是文件,或者是查看目录下孩子。
const http= require('http')
const url = require('url')
const path = require('path')
const fs = require('fs').promises
const {createReadStream}=require('fs')
const mime=require('mime')
const chalk = require('chalk')
const nunjucks=require('nunjucks')

class Server{
    constructor(config){
        this.port = config.port
        this.dir = config.dir
        
    }
    sendFile(currentpath,res){
        res.setHeader('Content-Type',mime.getType(currentpath)+';charset=utf8')
        createReadStream(currentpath).pipe(res)//是文件就做成流返回去
    }
    async handleRequest(req,res){
        let {pathname}=url.parse(req.url)//中文名会自动编码
        pathname=decodeURIComponent(pathname)//解码中文名
        let absPath =path.join(this.dir,pathname)
        try{
            let statObj= await fs.stat(absPath)//判断路径是目录还是文件
            if(statObj.isFile()){//是文件
                this.sendFile(absPath,res)
            }else{//列出所有文件夹的内容
                let children =await fs.readdir(absPath)
                children= children.map((item)=>{
                    return{
                        current:item,
                        parent :path.join(pathname,item)
                    }
                })
                nunjucks.configure(path.resolve(__dirname))
                let template = nunjucks.render('template.html',{
                    items:children
                })
                res.setHeader('Content-Type','text/html;charset=utf8')
                res.end(template)
            }
        }catch(e){
            console.log(e);
            this.sendError(res)
        }
    }
    sendError(res){
        res.statusCode=404
        res.end('Not Fond')
    }
    start(){
        let server = http.createServer(this.handleRequest.bind(this))
        server.listen(this.port,()=>{
            console.log(`${chalk.yellow('Starting up http-server, serving') }./${this.dir.split('\\').pop()}
            Available on:
            http://127.0.0.1:${chalk.green(this.port)}
            Hit CTRL-C to stop the server`)
        })
    }
}

module.exports =Server
  • 模板如下,很简单就是个列表循环。这个模板自称继承jinjia2的衣钵,语法和jinjia2是一样的。
<!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>Document</title>
</head>
<body>
    <ul>
        {% for item in items %}
            <li><a href="{{item.parent}}">{{ item.current }}</a></li>
        {% endfor %}
    </ul>
</body>
</html>
  • 最后进行发布
  • 这个nunjunks如果不设置configure的绝对路径容易找不到模板,虽然文档写默认是当前目录,但实际路径对的但可能内部处理有bug,必须设置下绝对路径。
  • 这个包我发到npm上,有需要自取:npm i ye-staticserver -g 启动直接ye-static 默认3000端口当前目录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

业火之理

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值