zoukankan      html  css  js  c++  java
  • 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
     ${ex.error}`)
        }
    }

    主要代码是

     运行结果

    首次

     刷新

  • 相关阅读:
    饿了么P7级前端工程师进入大厂的面试经验
    前端程序员面试的坑,简历写上这一条信息会被虐死!
    这次来分享前端的九条bug吧
    移动端开发必会出现的问题和解决方案
    创建一个dynamics 365 CRM online plugin (八)
    创建一个dynamics 365 CRM online plugin (七)
    创建一个dynamics 365 CRM online plugin (六)
    创建一个dynamics 365 CRM online plugin (五)
    使用User Primary Email作为GUID的问题
    怎样Debug Dynamics 365 CRM Plugin
  • 原文地址:https://www.cnblogs.com/chorkiu/p/11431199.html
Copyright © 2011-2022 走看看