NodeJS4-7静态资源服务器实战_缓存

浏览器发出一个请求,服务器解析出响应的结果返回给浏览器.

缓存是怎么工作的?

用户发起请求,浏览器检查本地是否存在缓存,如果第一次请求没有缓存,那就向服务器发起请求,服务器协商缓存的内容并且返回响应,接着返回缓存响应,再次请求时,会检查缓存是否失效,没有失效就使用本地缓存,如果本地缓存失效了,

 

 缓存header

 判断本地有没有失效的可以用

  • 返回时间Expires/Cache-Control

  Expires(比较老式):返回的是一个绝对时间,由于时区问题很少用

  Cache-Control(常用):返回的是一个相对时间

  • 修改时间Last-Modified/If-Modified_Since

  Last-Modified:向服务器校验的时候拿到的结果,每次返回响应的时候会告诉Last-Modified时间

  If-Modified_Since:浏览器第二次或者第三次发起请求时,会把上次的修改时间放在修改头的If-Modified_Since

  • 服务器校验 If-None-Match /TRag

defaultConfig.js

module.exports={
    root:process.cwd(),
    hostname:'127.0.0.1',
    port:9527,
    compress:/\.(html|js|css|md)/,
    cache:{
        maxAge:600,
        expire:true,
        cacheControl:true,
        lastModified:true,
        etag:true
    }
}

新建文件src/helper/cache.js

const {cache} = require('../config/defaultConfig')
// 更新一下响应,修改时间
function refreshRes(stats,res){
    const {maxAge,expires,cacheControl,lastModified,etag} = cache;

    if(expires){
        res.setHeader('Expires',(new Date(Date.now() + maxAge *1000)).toUTCString())
    }
    if(cacheControl){
        res.setHeader('Cache-Control',`public,max-age=${maxAge}`)
    }
    if(lastModified){
        res.setHeader('Last-Modified',stats.mtime.toUTCString())
    }
    if(etag){
        res.setHeader('ETag',`${stats.size} = ${stats.mtime}`);
    }

}

module.exports = function isFresh(stats,req,res){
    refreshRes(stats,res)
    const lastModified = req.headers['if-modified-since']
    const etag = req.headers['if-none-match']
    if(!lastModified && !etag){
        return false
    }
    if(lastModified && lastModified !==res.getHeader('Last-Modified')) {
        return false
    }   
    if(etag && etag !== res.getHeader('Etag')){
        return false
    }
    return true 
}

route.js引用ca'ch

const fs =require('fs')
const path = require('path')
const Handlebars = require('handlebars')
const promisify = require('util').promisify;
const stat = promisify(fs.stat)
const readdir = promisify(fs.readdir);
// //引用range范围
// const range = require('./range')
const config = require('../config/defaultConfig')
const tplPath = path.join(__dirname,'../template/dir.tpl')
const source = fs.readFileSync(tplPath);
const template = Handlebars.compile(source.toString())
//引入新加的mime,对contentType的判断
const mime = require('./mime')
const compress = require('./compress')

//引用range范围
const range = require('./range')

// 引入cache
const isFresh = require('./cache')


module.exports=async function(req,res,filePath){
    try{
        const stats =await stat(filePath)
        if(stats.isFile()){
            const contentType = mime(filePath)
            res.statusCode = 200
            res.setHeader('content-Type',contentType)
            
            if(isFresh(stats,req,res)){
                res.statusCode = 304;
                res.end()
                return 
            }

            let rs;
            const {code,start,end} = range(stats.size, req, res)
            if(code === 200){
                res.statusCode = 200
                rs = fs.createReadStream(filePath) 
            }else{
                res.statusCode = 216 //测试随便定
                rs = fs.createReadStream(filePath,{start,end}) 
            }

            // let rs = fs.createReadStream(filePath) 
            if(filePath.match(config.compress)){
                rs = compress(rs,req,res)
            }
            rs.pipe(res);
            // fs.readFile(filePath,(err,data)=>{
            //     res.end(data)
            // });
        }else if(stats.isDirectory()){
            //所有异步调用必须用await
            const files =await readdir(filePath);
            res.statusCode = 200
            res.setHeader('content-Type','text/html')
            const dir = path.relative(config.root,filePath)
            const data = {
                title:path.basename(filePath),
                // dir:config.root,
                dir:dir?`/${dir}`:'',
                files:files.map(file=>{
                    return {
                        file,
                        icon:mime(file)
                    }
                })
            }
            res.end(template(data));
        }
    }catch(ex){
        console.error(ex);
        res.statusCode = 404
        res.setHeader('content-Type','text/plain')
        res.end(`${filePath} is not a directory or file\n ${ex.error}`)
    }
}

主要代码是

 

 运行结果

首次

 

 刷新

 

posted @ 2019-08-29 17:57  KIU的博客  阅读(526)  评论(0编辑  收藏  举报