实现一个简易的静态服务器

静态服务器就是网站把一些在通常操作下不会发生改变的资源给浏览器。显示网站外观的图片和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模板引擎,内容为

<h2><%=name%></h2>
<%arr.forEach(item=>{%>
    <li><a href="<%=item.filepath%>"><%=item.filename%></a></li>
<%})%>

缓存功能也很简单,就是设置一些响应头,给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 <n>', 'config port')
    .option('-o,--host [value]', 'config host')

program.parse(process.argv);

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

let server = new Server(program);
server.start();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
为了实现一个简单的静态服务器,你可以使用以下步骤: 1. 导入所需的依赖库(如`http`和`fs`)。 2. 创建一个 HTTP 服务器对象。 3. 监听指定的端口号,以便服务器能够接收请求。 4. 在请求到达时,解析请求中的 URL,并获取请求的文件路径。 5. 使用 `fs` 模块读取文件内容。 6. 根据文件类型设置正确的响应头。 7. 将文件内容作为响应主体发送回客户端。 8. 处理错误情况,如文件不存在或读取文件时出错。 这是一个简单的示例代码,展示了如何创建一个静态服务器: ```javascript const http = require('http'); const fs = require('fs'); const path = require('path'); const server = http.createServer((req, res) => { // 解析请求的 URL 并获取文件路径 const filePath = path.join(__dirname, req.url); // 使用 fs 模块读取文件内容 fs.readFile(filePath, (err, data) => { if (err) { // 处理文件不存在或读取错误的情况 res.writeHead(404); res.end('File not found!'); } else { // 根据文件类型设置正确的响应头 const extname = path.extname(filePath); let contentType = 'text/plain'; if (extname === '.html') { contentType = 'text/html'; } else if (extname === '.css') { contentType = 'text/css'; } else if (extname === '.js') { contentType = 'text/javascript'; } // 发送响应 res.writeHead(200, { 'Content-Type': contentType }); res.end(data); } }); }); const port = 3000; server.listen(port, () => { console.log(`Server running on port ${port}`); }); ``` 你可以将上述代码保存为 `.js` 文件并运行,然后在浏览器中访问 `http://localhost:3000`,服务器将返回相应的静态文件。请注意,这只是一个简单的示例,不适用于生产环境,生产环境中通常会有更多的安全和性能考虑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值