Node.js一行代碼實(shí)現(xiàn)靜態(tài)文件服務(wù)器的方法步驟
靜態(tài)文件服務(wù)器實(shí)現(xiàn)
nodejs不僅僅可以用來(lái)寫(xiě)服務(wù)端接口,用來(lái)做靜態(tài)文件服務(wù)器替代nginx的功能, 也是分分鐘可以搞定的。 話(huà)不多說(shuō),先上代碼:
var server=http.createServer(function (req,res){ fs.createReadStream(Path.resolve(__dirname,"."+req.url)).pipe(res); })
在項(xiàng)目根目錄建一個(gè)hello.html文件測(cè)試一下 hello.html內(nèi)容如下:
<h1>hello,world</h1>
node app.js運(yùn)行,打開(kāi)瀏覽器訪(fǎng)問(wèn)一下: http://localhost/hello.html
我們?cè)倩仡^審視一下代碼,的確就只有這么簡(jiǎn)單,這要?dú)w功于node Stream類(lèi) pipe方法的強(qiáng)大,fs.createReadStream讀取本地文件創(chuàng)建一個(gè)可讀流(ReadStream類(lèi)的實(shí)例),再使用pipe導(dǎo)流到res響應(yīng)流,res是一個(gè)http.ServerResponse類(lèi)的實(shí)例,是一個(gè)可寫(xiě)流,繼承自 Stream類(lèi)
http.ServerResponse類(lèi)的繼承關(guān)系如下:
安全性考慮
上述代碼實(shí)現(xiàn)靜態(tài)文件服務(wù)器后,意味著項(xiàng)目根目錄下所有的文件(遞歸)都可以通過(guò)瀏覽器直接訪(fǎng)問(wèn)和下載了,這樣會(huì)帶來(lái)一些安全性的問(wèn)題,想想看,你的服務(wù)器端代碼和配置文件都能通過(guò)瀏覽器直接下載了,因此需要在代碼里加一些限制,例如只能訪(fǎng)問(wèn)特定的目錄下的文件和特定擴(kuò)展名的文件,這樣還不夠,參考OWasp Top 10安全風(fēng)險(xiǎn)(第4條-不安全的對(duì)象直接引用),攻擊者仍然可以通過(guò)../../目錄回溯的方法訪(fǎng)問(wèn)到其它目錄,對(duì)于訪(fǎng)問(wèn)路徑中包含..的也要全部過(guò)濾掉。
實(shí)現(xiàn)mine type
mime type是指http 響應(yīng)頭中的content-type字段,它決定了瀏覽器如何解析文件,是直接當(dāng)做純文件顯示(text/plain),還是做為html文件渲染(text/html),或者當(dāng)做二進(jìn)制文件下載,沒(méi)有輸出正確的mine type,可能導(dǎo)致圖片文件無(wú)法顯示,字體文件無(wú)效,視頻文件無(wú)法播放的問(wèn)題。要實(shí)現(xiàn)起來(lái)也十分簡(jiǎn)單,只需要做一個(gè)映射表,不同文件擴(kuò)展名,在響應(yīng)頭的content-type字段中輸出對(duì)應(yīng)的mine type就行了。
完整代碼如下:
const http=require("http"); const Path=require("path"); const fs=require("fs"); var server=http.createServer(function (req,res){ const fileName=Path.resolve(__dirname,"."+req.url); const extName=Path.extname(fileName).substr(1); if (fs.existsSync(fileName)) { //判斷本地文件是否存在 var mineTypeMap={ html:'text/html;charset=utf-8', htm:'text/html;charset=utf-8', xml:"text/xml;charset=utf-8", png:"image/png", jpg:"image/jpeg", jpeg:"image/jpeg", gif:"image/gif", css:"text/css;charset=utf-8", txt:"text/plain;charset=utf-8", mp3:"audio/mpeg", mp4:"video/mp4", ico:"image/x-icon", tif:"image/tiff", svg:"image/svg+xml", zip:"application/zip", ttf:"font/ttf", woff:"font/woff", woff2:"font/woff2", } if (mineTypeMap[extName]) { res.setHeader('Content-Type', mineTypeMap[extName]); } var stream=fs.createReadStream(fileName); stream.pipe(res); } }) server.listen(80);
實(shí)現(xiàn)gzip
對(duì)于文本類(lèi)型的文件,如html,js,css,采用gzip壓縮可以大幅減少傳輸量,提升服務(wù)器傳輸性能,當(dāng)然這會(huì)損耗一點(diǎn)服務(wù)器的cpu性能做為代價(jià),如果客戶(hù)端瀏覽器支持gzip壓縮,則會(huì)在請(qǐng)求頭的accept-encoding中攜帶gzip關(guān)鍵字,用node自帶的zlib類(lèi)就可以實(shí)現(xiàn)gzip壓縮了,只要在stream.pip實(shí)多加一層,先導(dǎo)流到gzip流,再導(dǎo)出到res流,當(dāng)然,還要在響應(yīng)頭中添加Content-Encoding為gzip,這樣瀏覽器才能正確識(shí)別到http body是采用gzip算法壓縮的,并進(jìn)行自動(dòng)解壓縮。
代碼如下:
const zlib = require('zlib'); if (req.headers["accept-encoding"].indexOf("gzip")>=0 && (extName=="js" || extName=="css" || extName=="html"))) { res.setHeader('Content-Encoding', "gzip"); const gzip = zlib.createGzip(); stream.pipe(gzip).pipe(res); }
客戶(hù)端緩存
http協(xié)議的緩存協(xié)商流程比較長(zhǎng),最終在響應(yīng)頭中生成expire(絕對(duì)時(shí)間)和cache-control(相對(duì)時(shí)間)兩個(gè)用于控制緩存過(guò)期時(shí)間的參數(shù),瀏覽器下次請(qǐng)求該文件時(shí),分為以下幾種情況:
- 如果沒(méi)到過(guò)期時(shí)間,瀏覽器不會(huì)請(qǐng)求文件直接讀緩存
- 如果已到過(guò)期時(shí)間,則會(huì)在請(qǐng)求頭中l(wèi)ast-modified字段攜帶文件的最后修改日期,如果對(duì)比時(shí)間戳與服務(wù)器文件一致,則HTTP 返回 304: Not Modified
- 如果按下f5刷新,會(huì)在請(qǐng)求頭中if-modified-since字段中攜帶緩存的過(guò)期時(shí)間,如果對(duì)比時(shí)間戳與服務(wù)器文件一致,則HTTP 返回 304: Not Modified
- ctrl+f5刷新,請(qǐng)求頭中攜帶 cache-control: no-cache,強(qiáng)制禁用緩存。重新下載文件
邏輯分支較多,但都是日期比對(duì),搞清楚緩存協(xié)商過(guò)程比較容易寫(xiě)出來(lái),有興趣的同學(xué)可以自行實(shí)現(xiàn)
高性能靜態(tài)文件服務(wù)器優(yōu)化
如果要做一個(gè)高性能的靜態(tài)文件服務(wù)器僅實(shí)現(xiàn)gzip和緩存協(xié)商是不夠的,涉及到本地文件的頻繁讀取,高并發(fā)下I/O必定成為瓶頸,考慮到服務(wù)器上的文件是很少更新的, 可以用Buffer把文件流緩存到內(nèi)存中,每次請(qǐng)求時(shí)先在內(nèi)存中查找匹配項(xiàng),如果命中了直接從內(nèi)存中返回,避免了讀取磁盤(pán),gzip也不用壓縮了,直接用壓縮好的文件流返回,可以成倍的大幅提升性能。當(dāng)然如果文件太多了,內(nèi)存也會(huì)飆升,需要考慮淘汰算法,只緩存訪(fǎng)問(wèn)次數(shù)高的文件,剔除低訪(fǎng)問(wèn)量的文件。
采用fs.watch監(jiān)控目錄文件的變化,如果文件有更新,則刪掉緩存。
小結(jié)
Node.js 內(nèi)置的pipe方法可以非常簡(jiǎn)便的實(shí)現(xiàn)將服務(wù)器本地文件輸出到http 響應(yīng)流中,gzip壓縮也同樣可以通過(guò)pipe實(shí)現(xiàn),再配合輸出mine type 實(shí)現(xiàn)的靜態(tài)服務(wù)器已經(jīng)可以滿(mǎn)足一般業(yè)務(wù)的使用。如果要實(shí)現(xiàn)高性能的靜態(tài)文件服務(wù)器,還需要實(shí)現(xiàn)客戶(hù)端緩存、服務(wù)端緩存功能(本文提供了思路,按圖索驥也非難事)。
最后,推薦一下個(gè)人的開(kāi)源項(xiàng)目, node.js web開(kāi)發(fā)框架,已包含本文靜態(tài)文件服務(wù)器的功能 webcontext: https://github.com/windyfancy/webcontext
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- node靜態(tài)服務(wù)器實(shí)現(xiàn)靜態(tài)讀取文件或文件夾
- Node4-5靜態(tài)資源服務(wù)器實(shí)戰(zhàn)以及優(yōu)化壓縮文件實(shí)例內(nèi)容
- 實(shí)戰(zhàn)node靜態(tài)文件服務(wù)器的示例代碼
- 用Nodejs搭建服務(wù)器訪(fǎng)問(wèn)html、css、JS等靜態(tài)資源文件
- 在windows上用nodejs搭建靜態(tài)文件服務(wù)器的簡(jiǎn)單方法
- Node.js靜態(tài)文件服務(wù)器改進(jìn)版
- 使用nodejs、Python寫(xiě)的一個(gè)簡(jiǎn)易HTTP靜態(tài)文件服務(wù)器
- 如何使用Node寫(xiě)靜態(tài)文件服務(wù)器
相關(guān)文章
推薦 21 款優(yōu)秀的高性能 Node.js 開(kāi)發(fā)框架
Node.js是JavaScript中最為流行的框架之一,易于創(chuàng)建可擴(kuò)展的Web應(yīng)用。Node.js包含不同類(lèi)型框架,包括MVC, full-stack,REST API以及Generators。借助這些框架使Node.js更加易于使用,它還支持眾多特性功能,只需幾個(gè)步驟就可快速搭建強(qiáng)大的Web應(yīng)用。本文為大家推薦21款2014-08-08詳解nodejs微信公眾號(hào)開(kāi)發(fā)——1.接入微信公眾號(hào)
本篇文章主要介紹了詳解nodejs微信公眾號(hào)開(kāi)發(fā)——1.接入微信公眾號(hào),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-04-04nodejs實(shí)現(xiàn)的連接MySQL數(shù)據(jù)庫(kù)功能示例
這篇文章主要介紹了nodejs實(shí)現(xiàn)的連接MySQL數(shù)據(jù)庫(kù)功能,結(jié)合實(shí)例形式分析了nodejs連接及查詢(xún)mysql數(shù)據(jù)的相關(guān)操作步驟與實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-01-01Node.js讀寫(xiě)文件之批量替換圖片的實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇Node.js讀寫(xiě)文件之批量替換圖片的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-09-09Node4-5靜態(tài)資源服務(wù)器實(shí)戰(zhàn)以及優(yōu)化壓縮文件實(shí)例內(nèi)容
這篇文章主要介紹了Node4-5靜態(tài)資源服務(wù)器實(shí)戰(zhàn)以及優(yōu)化壓縮文件實(shí)例內(nèi)容,有需要的朋友們可以參考學(xué)習(xí)下。2019-08-08利用pm2部署多個(gè)node.js項(xiàng)目的配置教程
目前似乎最常見(jiàn)的線(xiàn)上部署nodejs項(xiàng)目的有forever,pm2這兩種,而下面這篇文章主要給大家介紹了關(guān)于利用pm2部署多個(gè)node.js項(xiàng)目的配置教程,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-10-10nestjs中異常過(guò)濾器Exceptionfilter的具體使用
這篇文章主要介紹了nestjs中異常過(guò)濾器Exceptionfilter的具體使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02nodejs body-parser 解析post數(shù)據(jù)實(shí)例
下面小編就為大家?guī)?lái)一篇nodejs body-parser 解析post數(shù)據(jù)實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07