成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Node構(gòu)建一個靜態(tài)文件服務(wù)器

banana_pi / 326人閱讀

摘要:第二次客戶端需要此數(shù)據(jù)的時候,要取得緩存的標(biāo)識,然后去問一下服務(wù)器我的資源是否是最新的。服務(wù)器收到請求,將服務(wù)器的中此文件的跟請求頭中的相比較如果值是一樣的說明緩存還是最新的服務(wù)器將發(fā)送響應(yīng)碼給客戶端表示緩存未修改過,可以使用。

靜態(tài)文件服務(wù)器實(shí)現(xiàn)的功能
讀取靜態(tài)文件 
MIME類型支持
緩存支持/控制
支持gzip壓縮
Range支持,斷點(diǎn)續(xù)傳
發(fā)布為可執(zhí)行命令并可以后臺運(yùn)行,可以通過npm install -g安裝

首先先構(gòu)建好項(xiàng)目目錄,項(xiàng)目目錄如下:
project
|---bin 命令行實(shí)現(xiàn)放置腳本
|
|---public 靜態(tài)文件服務(wù)器默認(rèn)靜態(tài)文件夾
|
|---src 實(shí)現(xiàn)功能的相關(guān)代碼
| |
| |__template 模板文件夾
| |
| |__app.js 主要功能文件(main文件)
| |__config.js 配置文件
|
|---package.josn (初始化)

要啟動一個服務(wù)器,我們需要知道這個服務(wù)器的啟動時的端口號,在config.js配置一下:

let config = {
 host:"localhost" //提示用 ,
port:8080 //服務(wù)器啟動時候的默認(rèn)端口號,
path:path.resolve(__dirname,"..","test-dir") //靜態(tài)服務(wù)器啟動時默認(rèn)的工作目錄
 }

讀取靜態(tài)文件之前首先要先啟動服務(wù)器,之后所有的方法都在class Server方法里

//handlebar 編譯模板,得到一個渲染的方法,然后傳入實(shí)際數(shù)據(jù)數(shù)據(jù)就可以得到渲染后的HTML了
function list() {
let tmpl = fs.readFileSync(path.resolve(__dirname, "template", "list.html"),"utf8");
return handlebars.compile(tmpl);//進(jìn)行編譯,最后渲染
}class Server {
    constructor(argv) {
        this.list = list();
        this.config = Object.assign({}, this.config, argv);
    }
    start() {
        let server = http.createServer();//創(chuàng)建服務(wù)器
        //當(dāng)客戶端向服務(wù)端發(fā)出數(shù)據(jù)的時候,會出發(fā)request事件
        server.on("request", this.request.bind(this));
        server.listen(this.config.port, () => {//監(jiān)聽端口號
            let url = `http://${this.config.host}:${this.config.port}`;
            debug(`server started at ${chalk.green(url)}`);
        });
    }
//發(fā)送錯誤信息,
sendError(err, req, res) {
    res.statusCode = 500;
    res.end(`${err.toString()}`);
}}
module.exports = Server;
讀取靜態(tài)文件
設(shè)計思路
首先輸入一個url時,可能對應(yīng)服務(wù)器上的一個文件,或者對應(yīng)一個目錄,
檢查是否文件還是目錄如果文件不存在,返回404狀態(tài)碼,發(fā)送not found頁面到客戶端
如果文件存在:打開文件讀取
設(shè)置response header 發(fā)送文件到客戶端
如果是目錄就打開目錄列表
async request(req, res) {
    //先取到客戶端想要的是文件或文件夾路徑 
    let { pathname } = url.parse(req.url);//獲取路徑的文件信息
    let filepath = path.join(this.config.root, pathname);//服務(wù)器上的對應(yīng)服務(wù)器物理路徑
    try {
        let statObj = await stat(filepath);//獲取路徑的文件信息
        if (statObj.isDirectory()) {//如果是目錄的話,應(yīng)該顯示目錄 下面的文件列表
            let files = await readdir(filepath);//讀取文件的文件列表
            files = files.map(file => ({//把每個字符串變成對象
                name: file,
                url: path.join(pathname, file)
            }));
            //handlebar 編譯模板
            let html = this.list({
                title: pathname,
                files
            });
            res.setHeader("Content-Type", "text/html");設(shè)置請求頭
            res.end(html);
        } else {
            this.sendFile(req, res, filepath, statObj);//讀取文件
        }
    } catch (e) {//不存在訪問內(nèi)就發(fā)送錯誤信息
        debug(inspect(e));//inspect把一個對象轉(zhuǎn)成字符
        this.sendError(e, req, res);
    }
}

緩存支持/控制

設(shè)計思路
緩存分為強(qiáng)制緩存和對比緩存:?

兩類緩存規(guī)則可以同時存在,強(qiáng)制緩存優(yōu)先級高于對比緩存,也就是說,當(dāng)執(zhí)行強(qiáng)制緩存的規(guī)則時,如果緩存生效,直接使用緩存,不再執(zhí)行對比緩存規(guī)則.

?強(qiáng)制緩存如果生效,不需要再和服務(wù)器發(fā)生交互,而對比緩存不管是否生效,都需要與服務(wù)端發(fā)生交互

第一次訪問服務(wù)器的時候,服務(wù)器返回資源和緩存的標(biāo)識,客戶端則會把此資源緩存在本地的緩存數(shù)據(jù)庫中。

第二次客戶端需要此數(shù)據(jù)的時候,要取得緩存的標(biāo)識,然后去問一下服務(wù)器我的資源是否是最新的。如果是最新的則直接使用緩存數(shù)據(jù),如果不是最新的則服務(wù)器返回新的資源和緩存規(guī)則,客戶端根據(jù)緩存規(guī)則緩存新的數(shù)據(jù)。

通過最后修改時間來判斷緩存是否可用

Last-Modified:響應(yīng)時告訴客戶端此資源的最后修改時間?

If-Modified-Since:當(dāng)資源過期時(使用Cache-Control標(biāo)識的max-age),發(fā)現(xiàn)資源具有Last-Modified聲明,則再次向服務(wù)器請求時帶上頭If-Modified-Since。

服務(wù)器收到請求后發(fā)現(xiàn)有頭If-Modified-Since則與被請求資源的最后修改時間進(jìn)行比對。若最后修改時間較新,說明資源又被改動過,則響應(yīng)最新的資源內(nèi)容并返回200狀態(tài)碼;?

若最后修改時間和If-Modified-Since一樣,說明資源沒有修改,則響應(yīng)304表示未更新,告知瀏覽器繼續(xù)使用所保存的緩存文件。

ETag是資源標(biāo)簽。如果資源沒有變化它就不會變。

1.客戶端想判斷緩存是否可用可以先獲取緩存中文檔的ETag,然后通過If-None-Match發(fā)送請求給Web服務(wù)器詢問此緩存是否可用。
2?服務(wù)器收到請求,將服務(wù)器的中此文件的ETag,跟請求頭中的If-None-Match相比較,如果值是一樣的,說明緩存還是最新的,Web服務(wù)器將發(fā)送304 Not Modified響應(yīng)碼給客戶端表示緩存未修改過,可以使用。?
3.如果不一樣則Web服務(wù)器將發(fā)送該文檔的最新版本給瀏覽器客戶端

handleCache(req, res, filepath, statObj) {
    let ifModifiedSince = req.headers["if-modified-since"];
    let isNoneMatch = req.headers["is-none-match"];
    res.setHeader("Cache-Control", "private,max-age=30");//max-age=30緩存內(nèi)容將在30秒后失效
    res.setHeader("Expires", new Date(Date.now() + 30 * 1000).toGMTString());
    let etag = statObj.size;
    let lastModified = statObj.ctime.toGMTString();
    res.setHeader("ETag", etag);//獲取ETag
    res.setHeader("Last-Modified", lastModified);//服務(wù)器文件的最后修改時間
    //任何一個對比緩存頭不匹配,則不走緩存
    if (isNoneMatch && isNoneMatch != etag) {//緩存過期
        return fasle;
    }
    if (ifModifiedSince && ifModifiedSince != lastModified) {//緩存過期
        return fasle;
    }
    //當(dāng)請求中存在任何一個對比緩存頭,則返回304,否則不走緩存
    if (isNoneMatch || ifModifiedSince) {//緩存有效
        res.writeHead(304);
        res.end();
        return true;
    } else {
        return false;
    }
}
支持gzip壓縮
設(shè)計思路

瀏覽器都會攜帶自己支持的壓縮類型,最常用的兩種是gzip和deflate。根據(jù)請求頭Accept-Encoding,返回不同的壓縮格式.

getEncoding(req, res) {

    let acceptEncoding = req.headers["accept-encoding"];//獲取客戶端發(fā)送的壓縮請求頭的信息
    if (/gzip/.test(acceptEncoding)) {//如果是gzip的格式
        res.setHeader("Content-Encoding", "gzip");
        return zlib.createGzip();
    } else if (/deflate/.test(acceptEncoding)) {//如果是deflate的格式
        res.setHeader("Content-Encoding", "deflate");
        return zlib.createDeflate();
    } else {
        return null;//不壓縮
    }
}
Range支持,斷點(diǎn)續(xù)傳

設(shè)計思路

該選項(xiàng)指定下載字節(jié)的范圍,常應(yīng)用于分塊下載文件

服務(wù)器告訴客戶端可以使用range response.setHeader("Accept-Ranges", "bytes") ?

Server通過請求頭中的Range:bytes=0-xxx來判斷是否是做Range請求,如果這個值存在而且有效,則只發(fā)回請求的那部分文件內(nèi)容,響應(yīng)的狀態(tài)碼變成206,如果無效,則返回416狀態(tài)碼,表明Request

Range Not Satisfiable

?getStream(req, res, filepath, statObj) {
    let start = 0;//可讀流起始位置
    let end = statObj.size - 1;//可讀流結(jié)束位置
    let range = req.headers["range"];//獲取客戶端的range請求頭信息,
    if (range) {//斷點(diǎn)續(xù)傳
        res.setHeader("Accept-Range", "bytes");
        res.statusCode = 206;//返回整個內(nèi)容的一塊
        let result = range.match(/bytes=(d*)-(d*)/);//斷點(diǎn)續(xù)傳的分段內(nèi)容不能有小數(shù),網(wǎng)絡(luò)傳輸?shù)淖钚挝粸橐粋€字節(jié)
        if (result) {
            start = isNaN(result[1]) ? start : parseInt(result[1]);
            end = isNaN(result[2]) ? end : parseInt(result[2]) - 1;
        }
    }
    return fs.createReadStream(filepath, {
        start, end
    });
}

發(fā)布為可執(zhí)行命令

首先在package.json配置一下"bin": { "http-static": "bin/www" }

#! /usr/bin/env node     //這段代碼一定要寫在開頭,為了兼容各個電腦平臺的差異性
// -d --root 靜態(tài)文件目錄 -o --host 主機(jī) -p --port 端口號let yargs = require("yargs");
let  Server = require("../src/app.js");
let argv = yargs.option("d",{   
 alias:"root", 
 demand:"false",  
 type:"string",   
 default:process.cwd(),  
 description:"靜態(tài)文件跟目錄"    })
.option("o",{  
  alias:"host",  
  demand:"localhost",  
  type:"string",    
description:"請配置監(jiān)聽的主機(jī)"})
.option("p",{  
  alias:"root",  
  demand:"false",   
 type:"number",   
 default:8080,  
  description:"請配置端口號"})
.usage("http-static [options]").example(  
  "http-static -d / 8080 -o localhost","在本機(jī)的9090端口上監(jiān)聽客戶端的請求"
).help("h").argv;
// argv = {d,root,o,host,p,port}let server = new Server(argv);//啟動服務(wù)server.start();

這樣命令行當(dāng)中通過輸入http-static來直接啟動靜態(tài)文件服務(wù)器了,那么命令行調(diào)用的功能也就實(shí)現(xiàn)了,最后用npm publish發(fā)布一下,發(fā)布到npm上面去了,我們就可以通過npm install -g來進(jìn)行全局安裝了

參考資料

Node.js靜態(tài)文件服務(wù)器實(shí)戰(zhàn)

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/93555.html

相關(guān)文章

  • 我的博客發(fā)布上線方案 — Hexo

    摘要:首發(fā)于樊浩柏科學(xué)院之前一直在使用推薦的發(fā)布方案,缺點(diǎn)是本地依賴環(huán)境,無法隨時隨地地更新博客。為了擺脫環(huán)境約束進(jìn)而高效寫作,有了下述的發(fā)布方案。我的寫作環(huán)境為,博客發(fā)布在阿里云的上,文章托管在。 首發(fā)于 樊浩柏科學(xué)院 之前一直在使用 Hexo 推薦的發(fā)布方案,缺點(diǎn)是本地依賴 Hexo 環(huán)境,無法隨時隨地地更新博客。為了擺脫 Hexo 環(huán)境約束進(jìn)而高效寫作,有了下述的發(fā)布方案。 show...

    yangrd 評論0 收藏0
  • 我的博客發(fā)布上線方案 — Hexo

    摘要:首發(fā)于樊浩柏科學(xué)院之前一直在使用推薦的發(fā)布方案,缺點(diǎn)是本地依賴環(huán)境,無法隨時隨地地更新博客。為了擺脫環(huán)境約束進(jìn)而高效寫作,有了下述的發(fā)布方案。我的寫作環(huán)境為,博客發(fā)布在阿里云的上,文章托管在。 首發(fā)于 樊浩柏科學(xué)院 之前一直在使用 Hexo 推薦的發(fā)布方案,缺點(diǎn)是本地依賴 Hexo 環(huán)境,無法隨時隨地地更新博客。為了擺脫 Hexo 環(huán)境約束進(jìn)而高效寫作,有了下述的發(fā)布方案。 show...

    Michael_Lin 評論0 收藏0
  • ThinkJS 項(xiàng)目構(gòu)建 Docker 鏡像

    摘要:而項(xiàng)目代碼會隨著需求變更而經(jīng)常變化。項(xiàng)目啟動后一般會有兩個目錄會寫入文件。 其實(shí)這個話題很簡單,不是很想寫這篇文章。不過的確還是有很多朋友在打包構(gòu)建部署上存在一些問題,恰巧最近使用 Docker 部署了幾個 ThinkJS 相關(guān)的項(xiàng)目,所以還是拿出來說說吧。需要提前說明的是本文并不是 Docker 的基礎(chǔ)教程,默認(rèn)大家都是了解 Docker 的。然后我會分享一下我覺得 ThinkJS ...

    fuyi501 評論0 收藏0
  • [手把手系列之]Docker 部署 vue 項(xiàng)目

    摘要:部署項(xiàng)目寫在前面作為輕量級虛擬化技術(shù),擁有持續(xù)集成版本控制可移植性隔離性和安全性等優(yōu)勢。容器可以被創(chuàng)建啟動停止刪除暫停等。重新運(yùn)行應(yīng)用容器直接基于鏡像來啟動容器,運(yùn)行命令將宿主機(jī)的掛載到容器的目錄上。Docker 部署 vue 項(xiàng)目 1.寫在前面: Docker 作為輕量級虛擬化技術(shù),擁有持續(xù)集成、版本控制、可移植性、隔離性和安全性等優(yōu)勢。本文使用Docker來部署一個vue的前端應(yīng)用,并盡...

    VPointer 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<