node靜態(tài)服務(wù)器tudo

實(shí)現(xiàn)以下功能
  • 讀取靜態(tài)文件或目錄
  • MIME類(lèi)型支持
  • 緩存支持/控制
  • 支持gzip壓縮
  • 訪問(wèn)目錄可以自動(dòng)尋找下面的index.html文件
  • Range支持,斷點(diǎn)續(xù)傳
  • 圖片防盜鏈
  • 后臺(tái)運(yùn)行

基本功能讀取靜態(tài)文件

此功能分為兩部分:

* 1、返回文件
    調(diào)用fs建立可讀流讀取文件返回客戶(hù)端
    fs.createReadStream(filepath).pipe(res);
    
* 2、返回目錄
    因?yàn)槭且祷禺?dāng)前請(qǐng)求路徑下的所有目錄,并且支持點(diǎn)擊進(jìn)入下一級(jí),所有返回文件為html
    采用handlebars模板引擎編輯模板
    
    // 處理模板函數(shù)
    let list = function () {
        let template = fs.readFileSync(listTemplatePath, 'utf8');
        return handlebars.compile(template);
    }
    
    // template 文件
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>{{title}}</title>
    </head>
    <body>
        <ul>
            {{#each files}}
                <li>
                    <a href={{url}}>{{name}}</a>
                </li>
            {{/each}}
        </ul>
    </body>
    </html>
    
    // 讀取當(dāng)前路徑下的所有目錄
    let { promisify, inspect } = util;
    // 轉(zhuǎn)變fs.readdir 異步方法返回promise
    let readdirPromise = promisify(fs.readdir);
    // 返回filepath下所有目錄
    let files = await readdirPromise(filepath)
    // 處理files為我們想要的數(shù)據(jù)
    files = files.map(file => ({
        name: file,
        url: path.join(pathname, file),
    }))
    
    // 最終返回客戶(hù)端的html
    // list
    let html = this.list({
        title: pathname,
        files,
    });

MIME類(lèi)型支持

 res.setHeader('Content-Type', mime.getType(filepath));

緩存支持/控制

toCache (req, res, filepath, stat) {
        // 強(qiáng)制緩存
        res.setHeader('Cache-Control', 'private,max-age=60'); // http 1.1
        // private 客戶(hù)端可以緩存
        // public 客戶(hù)端和代理服務(wù)器都可以緩存
        // max-age=60 緩存內(nèi)容將在60秒后失效
        // no-cache 需要使用對(duì)比緩存驗(yàn)證數(shù)據(jù),強(qiáng)制向源服務(wù)器再次驗(yàn)證
        // no-store 所有內(nèi)容都不會(huì)緩存,強(qiáng)制緩存和對(duì)比緩存都不會(huì)觸發(fā) 
        res.setHeader('Expires', new Date(Date.now() + 60 * 1000).toUTCString()); // http 1.0


        // 對(duì)比緩存
        // last-modify
        let ifModifiedSince = req.headers['if-modified-since'];
        let lastModified = stat.ctime.toGMTString();
        // etag
        let ifNoneMatch = req.headers['if-none-match'];
        let eTag = cypto.createHash('sha1').update(stat.ctime.toGMTString() + stat.size).digest('hex');
        if (ifModifiedSince || ifNoneMatch) {
            if (ifNoneMatch === eTag && lastModified === ifModifiedSince) {
                res.statusCode = 304;
                res.end('');
                return false;
            }
            if ((ifModifiedSince && lastModified === ifModifiedSince) || (ifNoneMatch && ifNoneMatch === eTag)) {
                res.statusCode = 304;
                res.end('');
                return false;
            }
        }
        res.setHeader('Last-Modified', lastModified);
        res.setHeader('ETag', eTag);
        return true;
    }

壓縮支持

async gzip(req, res, gzipInfo) {
        let encoding = req.headers['accept-encoding'];
        let gzipReg = /\bgzip\b/;
        let deflateReg = /\bdeflate\b/;
        let type, streamInfo;  
        if (gzipReg.test(encoding)) {
            streamInfo = gzipInfo ? await gzipPromise(gzipInfo) : zlib.createGzip();
            type = 'gzip';
        } else if (deflateReg.test(encoding)) {
            streamInfo = gzipInfo ? await deflatePromise(gzipInfo) : zlib.createDeflate();
            type = 'deflate';
        }
        if (type) {
            res.setHeader('Content-Encoding', type);
        }
        return streamInfo;
    }

訪問(wèn)目錄可以自動(dòng)尋找下面的index.html文件

let indexFilepath = path.join(filepath, '/', 'index.html');
try {
    let statIndex = await statPromise(indexFilepath);
    if (statIndex) {
        // 返回 inde.html文件
        return this.sendFile(req, res, indexFilepath, stat);
    }
} catch (e) {}

Range支持,斷點(diǎn)續(xù)傳

range (req, res, filepath, stat) {
        res.setHeader('Accept-Range', 'bytes'); // 通知客戶(hù)端支持獲取部分資源
        let range = req.headers['range']; // Range: bytes=0-xxx
        let start = 0, end = stat.size;
        if (range) {
            let result = range.match(/bytes=(\d*)-(\d*)/);
            start = isNaN(result[1]) ? result[1] : start;
            end = isNaN(result[2]) ? result[2] : end;
        }
        return fs.createReadStream(filepath, {
            start,
            end,
        })
    }

圖片防盜鏈

notSteal (req, res) {
    console.log(path.join(process.cwd(), 'imgs/load.png'))
    let refer = req.headers['referer'] || req.headers['refer'];
    //如果說(shuō)有refer的話(huà),則表示是從HTML頁(yè)面中引用過(guò)來(lái)的
    if (refer) {
        let { host } = Url.parse(refer);
        if (host !== hostDomain) {
            // 返回默認(rèn)圖
            return fs.createReadStream(path.join(process.cwd(), 'imgs/load.png'));
        }
    }
}

開(kāi)啟子進(jìn)程運(yùn)行

let { spawn } =  require('child_process');
let fs = require('fs');

let serverPath = '../src/child.js';
let child = spawn('node', [serverPath], {
    detached: true,
    stdio: ['ignore', process.stdout, 'ignore']
})

child.unref();
  • 功能尚不完善,之后會(huì)慢慢豐富!
  • 更多細(xì)節(jié)源碼請(qǐng)查看我的GitHub sevenStatic

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.ngnix介紹 ngnix www服務(wù)軟件 俄羅斯人開(kāi)發(fā) 開(kāi)源 性能很高 本身是一款靜態(tài)WWW軟件 靜態(tài)小文件...
    逗比punk閱讀 2,236評(píng)論 1 6
  • 支持功能: 讀取靜態(tài)文件 訪問(wèn)目錄可以自動(dòng)尋找下面的index.html文件, 如果沒(méi)有index.html則列出...
    lihuanji閱讀 448評(píng)論 0 0
  • 這篇文章主要將會(huì)通過(guò)node手把手的構(gòu)建一個(gè)靜態(tài)文件服務(wù)器,那么廢話(huà)不多說(shuō),開(kāi)發(fā)流程走起來(lái),我們先看一下將要做的這...
    嘿_那個(gè)誰(shuí)閱讀 1,215評(píng)論 0 0
  • express功能極簡(jiǎn),主要是路由和中間件構(gòu)成,在本質(zhì)上,其實(shí)就是調(diào)用各種中間件。 我先把API簡(jiǎn)單的列出來(lái): e...
    sinbad_3815閱讀 351評(píng)論 0 4
  • 世界上的事物可以分為這樣幾類(lèi):我們知道我們知道的,我們知道我們不知道的;此外,還有我們不知道我們知道的,以及我們不...
    Am王小強(qiáng)閱讀 206評(píng)論 0 0

友情鏈接更多精彩內(nèi)容