• 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}`)
        }
    }

    主要代码是

     运行结果

    首次

     刷新

  • 相关阅读:
    Jquery 操作radio实例
    不错的linux下通用的java程序启动脚本(转载)
    activeMQ消费消息时网络流量异常大的问题
    利用passssh,批量远程修改机器密码
    tomcat的maxThreads、acceptCount(最大线程数、最大排队数)
    非阻塞IO、NIO为什么会快,我们为什么需要多线程
    关于find命令 name 匹配字符无法正确生效的问题 find: paths must precede expression:
    想取得刚才nextval()的值时,放心大胆的用currval()吧,currval()的返回值并不会因为nextval()的并发访问而混乱
    slef4j的好处
    杂(一) 奶酪
  • 原文地址:https://www.cnblogs.com/chorkiu/p/11431199.html
Copyright © 2020-2023  润新知