博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
实现一个简易的静态服务器
阅读量:6071 次
发布时间:2019-06-20

本文共 4364 字,大约阅读时间需要 14 分钟。

静态服务器就是网站把一些在通常操作下不会发生改变的资源给浏览器。显示网站外观的图片和CSS文件,在浏览器中运行的JavaScript代码,没有动态组件的HTML文件就是这种资源中的代表,统称为静态文件。

这里我用node来开发一个服务器,提供的其他功能有:

  1. 缓存
  2. 压缩
  3. 解析用户命令行输入

我们会用到的node包有:http,fs,path,url,zlib

http是提供web服务的核心包fs是文件或文件夹操作包path是将请求的路径转换成本机路径url是将请求的网址转换成对象,方便我们调用zlib是压缩包复制代码

我们还会用到一些第三方包:ejs,mime,commander

ejs是一个javascript的模板引擎mime是文件类型判断包commander用来解析用户在命令行输入的参数复制代码

我们的静态服务器的实现思路就是,启动一个静态服务器,监听用户发送的请求,当请求到来时,解析拿到请求的地址。如果请求的是文件,就读取相应的文件并返回给用户。如果的文件夹,就读取文件夹下的所有文件名,然后把文件名放在模板html里并返回给用户。

class Server{    constructor(opts = {}){        this.host = opts.host || '127.0.0.1'        this.port = opts.port || 3000        this.staticPath = opts.staticPath || 'public'    }    start(){        let server = http.createServer(this.handleRequest.bind(this));        server.listen(this.port, this.host, ()=>{            console.log(`服务已启动:${this.host}:${this.port}`)        })    }    handleRequest(req, res){        let self = this;        //获取请求的文件名        let {pathname} = url.parse(req.url);        if(pathname == '/favicon.ico') return res.end();        //把请求的文件名转换成public下的绝对路径        let p = path.join(__dirname, '../', this.staticPath, pathname);        fs.stat(p, function(err, stats){            if(err){                res.end(err);            }            if(stats.isDirectory()){                self.sendDir(req, res, stats, p);            }else{                self.sendFile(req, res, stats, p);            }        })    }    sendFile(req, res, stats, p){        res.setHeader('Content-Type', mime.getType(p) + ';charset=utf-8')//发送的数据类型                fs.createReadStream(p).pipe(res)    }    sendDir(req, res, stats, p){        let {pathname} = url.parse(req.url);        res.setHeader('Content-Type', 'text/html;charset=utf-8')//发送的数据类型        let template = fs.readFileSync(path.join(__dirname, 'template.html'), 'utf-8')        let files = fs.readdirSync(p)        files = files.map(file=>{            return {                filename: file,                filepath: path.join(pathname, file)            }        })        let str = ejs.render(template, {            name:`index of ${pathname}`,             arr:files,        })        res.end(str)    }}复制代码

template.html就是用来显示文件夹下的文件名,主要用到的就是ejs模板引擎,内容为

<%=name%>

<%arr.forEach(item=>{%>
  • <%=item.filename%>
  • <%})%>复制代码

    缓存功能也很简单,就是设置一些响应头,给Server类增加一个原型方法

    setCache(req, res, stats, p){    res.setHeader('Cache-Control', 'max-age=10')//缓存存活时间    res.setHeader('Expires', new Date(Date.now() + 10 * 1000).toGMTString())//缓存存活时间        let etag = stats.ctime.getTime() + '-' + stats.size;    let LastModified = stats.ctime.toGMTString();        let ifNoneMatch = req.headers['if-none-match']    let ifModifiedSince = req.headers['if-modified-since']//文件最后修改时间    res.setHeader('Last-Modified', LastModified)    res.setHeader('Etag', etag)        if(etag == ifNoneMatch && LastModified == ifModifiedSince){        return true;    }    return false;}复制代码

    在发送文件方法里开启缓存

    sendFile(req, res, stats, p){    res.setHeader('Content-Type', mime.getType(p) + ';charset=utf-8')//发送的数据类型        if(this.setCache(req, res, stats, p)){        res.statusCode = 304;        return res.end();    }    fs.createReadStream(p).pipe(res)}复制代码

    压缩功能跟缓存类似,增加方法

    gzip(req, res, stats, p){    let encoding = req.headers['accept-encoding']    if(encoding){        if(encoding.match(/\bgzip\b/)){            res.setHeader('Content-Encoding', 'gzip')//压缩类型            return zlib.createGzip();        }        if(encoding.match(/\bdeflate\b/)){            res.setHeader('Content-Encoding', 'deflate')//压缩类型            return zlib.createDeflate();        }        return false;    }else{        return false;    }}复制代码

    开启压缩功能

    sendFile(req, res, stats, p){    res.setHeader('Content-Type', mime.getType(p) + ';charset=utf-8')//发送的数据类型        if(this.setCache(req, res, stats, p)){        res.statusCode = 304;        return res.end();    }    let transform = this.gzip(req, res, stats, p);    if(transform){        return fs.createReadStream(p).pipe(transform).pipe(res)    }    fs.createReadStream(p).pipe(res)}复制代码

    至此我们的主要功能就实现了,但是我们的服务不够智能,比如端口固定是3000,这样会出现端口冲突的问题。我们可以用commander包来接收用户的配置,来动态修改端口。

    let program = require('commander')program    .option('-p,--port 
    ', 'config port') .option('-o,--host [value]', 'config host')program.parse(process.argv);复制代码

    启动服务的时候把program传进去就可以了

    let server = new Server(program);server.start();复制代码

    转载地址:http://ksbgx.baihongyu.com/

    你可能感兴趣的文章
    OpenCV编程->ROI区域保存为图片
    查看>>
    SS哥的crontab教程
    查看>>
    python 面向对象
    查看>>
    兼职议会
    查看>>
    2012.12.18
    查看>>
    rpm包管理和yum的使用
    查看>>
    Linux tmux
    查看>>
    在此处打开命令窗口问题
    查看>>
    中国红××××××C
    查看>>
    深度探索I/O完成端口
    查看>>
    年夏天针对中国市场推出廉价版的iPhone—iPhone Mini
    查看>>
    我的友情链接
    查看>>
    超详细PXE批量部署Linux
    查看>>
    Java爬虫实战(一):抓取一个网站上的全部链接
    查看>>
    交互组件微创新
    查看>>
    10本 Groovy/Grails 的书籍
    查看>>
    3.3 bash详解2
    查看>>
    【Android游戏开发之三】剖析 SurfaceView ! Callback以及SurfaceHolder!!
    查看>>
    No UserDatabase component found under key UserData
    查看>>
    二分查找算法
    查看>>