<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

        使用Node.js搭建靜態(tài)資源服務(wù)詳細(xì)教程

        來源:懂視網(wǎng) 責(zé)編:小采 時間:2020-11-27 22:33:41
        文檔

        使用Node.js搭建靜態(tài)資源服務(wù)詳細(xì)教程

        使用Node.js搭建靜態(tài)資源服務(wù)詳細(xì)教程:對于Node.js新手,搭建一個靜態(tài)資源服務(wù)器是個不錯的鍛煉,從最簡單的返回文件或錯誤開始,漸進(jìn)增強(qiáng),還可以逐步加深對http的理解。那就開始吧,讓我們的雙手沾滿網(wǎng)絡(luò)請求! Note: 當(dāng)然在項(xiàng)目中如果有使用express框架,用express.static一行代碼就可
        推薦度:
        導(dǎo)讀使用Node.js搭建靜態(tài)資源服務(wù)詳細(xì)教程:對于Node.js新手,搭建一個靜態(tài)資源服務(wù)器是個不錯的鍛煉,從最簡單的返回文件或錯誤開始,漸進(jìn)增強(qiáng),還可以逐步加深對http的理解。那就開始吧,讓我們的雙手沾滿網(wǎng)絡(luò)請求! Note: 當(dāng)然在項(xiàng)目中如果有使用express框架,用express.static一行代碼就可

        對于Node.js新手,搭建一個靜態(tài)資源服務(wù)器是個不錯的鍛煉,從最簡單的返回文件或錯誤開始,漸進(jìn)增強(qiáng),還可以逐步加深對http的理解。那就開始吧,讓我們的雙手沾滿網(wǎng)絡(luò)請求!

        Note:

        當(dāng)然在項(xiàng)目中如果有使用express框架,用express.static一行代碼就可以達(dá)到目的了:

        app.use(express.static('public'))

        這里我們要實(shí)現(xiàn)的正是express.static背后所做工作的一部分,建議同步閱讀該模塊源碼。

        基本功能

        不急著寫下第一行代碼,而是先梳理一下就基本功能而言有哪些步驟。

        1. 在本地根據(jù)指定端口啟動一個http server,等待著來自客戶端的請求
        2. 當(dāng)請求抵達(dá)時,根據(jù)請求的url,以設(shè)置的靜態(tài)文件目錄為base,映射得到文件位置
        3. 檢查文件是否存在
        4. 如果文件不存在,返回404狀態(tài)碼,發(fā)送not found頁面到客戶端
        5. 如果文件存在:
      1. 打開文件待讀取
      2. 設(shè)置response header
      3. 發(fā)送文件到客戶端
      4. 6.等待來自客戶端的下一個請求

        實(shí)現(xiàn)基本功能

        代碼結(jié)構(gòu)

        創(chuàng)建一個nodejs-static-webserver目錄,在目錄內(nèi)運(yùn)行npm init初始化一個package.json文件。

        mkdir nodejs-static-webserver && cd "$_"
        // initialize package.json
        npm init

        接著創(chuàng)建如下文件目錄:

        -- config
        ---- default.json
        -- static-server.js
        -- app.js
        default.json
        {
         "port": 9527,
         "root": "/Users/sheila1227/Public",
         "indexPage": "index.html"
        }

        default.js存放一些默認(rèn)配置,比如端口號、靜態(tài)文件目錄(root)、默認(rèn)頁(indexPage)等。當(dāng)這樣的一個請求http://localhost:9527/myfiles/抵達(dá)時. 如果根據(jù)root映射后得到的目錄內(nèi)有index.html,根據(jù)我們的默認(rèn)配置,就會給客戶端發(fā)回index.html的內(nèi)容。

        static-server.js

        const http = require('http');
        const path = require('path');
        const config = require('./config/default');
        class StaticServer {
         constructor() {
         this.port = config.port;
         this.root = config.root;
         this.indexPage = config.indexPage;
         }
        
         start() {
         http.createServer((req, res) => {
         const pathName = path.join(this.root, path.normalize(req.url));
         res.writeHead(200);
         res.end(`Requeste path: ${pathName}`);
         }).listen(this.port, err => {
         if (err) {
         console.error(err);
         console.info('Failed to start server');
         } else {
         console.info(`Server started on port ${this.port}`);
         }
         });
         }
        }
        module.exports = StaticServer;

        在這個模塊文件內(nèi),我們聲明了一個StaticServer類,并給其定義了start方法,在該方法體內(nèi),創(chuàng)建了一個server對象,監(jiān)聽rquest事件,并將服務(wù)器綁定到配置文件指定的端口。在這個階段,我們對于任何請求都暫時不作區(qū)分地簡單地返回請求的文件路徑。path模塊用來規(guī)范化連接和解析路徑,這樣我們就不用特意來處理操作系統(tǒng)間的差異。

        app.js

        const StaticServer = require('./static-server');
        (new StaticServer()).start();


        在這個文件內(nèi),調(diào)用上面的static-server模塊,并創(chuàng)建一個StaticServer實(shí)例,調(diào)用其start方法,啟動了一個靜態(tài)資源服務(wù)器。這個文件后面將不需要做其他修改,所有對靜態(tài)資源服務(wù)器的完善都發(fā)生在static-server.js內(nèi)。

        在目錄下啟動程序會看到成功啟動的log:

        > node app.js
        Server started on port 9527

        在瀏覽器中訪問,可以看到服務(wù)器將請求路徑直接返回了。

        路由處理

        之前我們對任何請求都只是向客戶端返回文件位置而已,現(xiàn)在我們將其替換成返回真正的文件:

         routeHandler(pathName, req, res) { 
         }
         start() {
         http.createServer((req, res) => {
         const pathName = path.join(this.root, path.normalize(req.url));
         this.routeHandler(pathName, req, res);
         }).listen(this.port, err => {
         ...
         });
         }

        將由routeHandler來處理文件發(fā)送。

        讀取靜態(tài)文件

        讀取文件之前,用fs.stat檢測文件是否存在,如果文件不存在,回調(diào)函數(shù)會接收到錯誤,發(fā)送404響應(yīng).

         respondNotFound(req, res) {
         res.writeHead(404, {
         'Content-Type': 'text/html'
         });
         res.end(`<h1>Not Found</h1><p>The requested URL ${req.url} was not found on this server.</p>`);
         }
        
         respondFile(pathName, req, res) {
         const readStream = fs.createReadStream(pathName);
         readStream.pipe(res);
         }
        
         routeHandler(pathName, req, res) {
         fs.stat(pathName, (err, stat) => {
         if (!err) {
         this.respondFile(pathName, req, res);
         } else {
         this.respondNotFound(req, res);
         }
         });
         }

        Note:

        讀取文件,這里用的是流的形式createReadStream而不是readFile,是因?yàn)楹笳邥诘玫酵暾募?nèi)容之前將其先讀到內(nèi)存里。這樣萬一文件很大,再遇上多個請求同時訪問,readFile就承受不來了。使用文件可讀流,服務(wù)端不用等到數(shù)據(jù)完全加載到內(nèi)存再發(fā)回給客戶端,而是一邊讀一邊發(fā)送分塊響應(yīng)。這時響應(yīng)里會包含如下響應(yīng)頭:

        Transfer-Encoding:chunked

        默認(rèn)情況下,可讀流結(jié)束時,可寫流的end()方法會被調(diào)用。

        MIME支持

        現(xiàn)在給客戶端返回文件時,我們并沒有指定Content-Type頭,雖然你可能發(fā)現(xiàn)訪問文本或圖片瀏覽器都可以正確顯示出文字或圖片,但這并不符合規(guī)范。任何包含實(shí)體主體(entity body)的響應(yīng)都應(yīng)在頭部指明文件類型,否則瀏覽器無從得知類型時,就會自行猜測(從文件內(nèi)容以及url中尋找可能的擴(kuò)展名)。響應(yīng)如指定了錯誤的類型也會導(dǎo)致內(nèi)容的錯亂顯示,如明明返回的是一張jpeg圖片,卻錯誤指定了header:'Content-Type': 'text/html',會收到一堆亂碼。

        雖然有現(xiàn)成的mime模塊可用,這里還是自己來實(shí)現(xiàn)吧,試圖對這個過程有更清晰的理解。

        在根目錄下創(chuàng)建mime.js文件:

        const path = require('path');
        const mimeTypes = {
         "css": "text/css",
         "gif": "image/gif",
         "html": "text/html",
         "ico": "image/x-icon",
         "jpeg": "image/jpeg",
         ...
        };
        const lookup = (pathName) => {
         let ext = path.extname(pathName);
         ext = ext.split('.').pop();
         return mimeTypes[ext] || mimeTypes['txt'];
        }
        module.exports = {
         lookup
        };

        該模塊暴露出一個lookup方法,可以根據(jù)路徑名返回正確的類型,類型以‘type/subtype'表示。對于未知的類型,按普通文本處理。

        接著在static-server.js中引入上面的mime模塊,給返回文件的響應(yīng)都加上正確的頭部字段:

         respondFile(pathName, req, res) {
         const readStream = fs.createReadStream(pathName);
         res.setHeader('Content-Type', mime.lookup(pathName));
         readStream.pipe(res);
         }

        重新運(yùn)行程序,會看到圖片可以在瀏覽器中正常顯示了。

        Note:

        需要注意的是,Content-Type說明的應(yīng)是原始實(shí)體主體的文件類型。即使實(shí)體經(jīng)過內(nèi)容編碼(如gzip,后面會提到),該字段說明的仍應(yīng)是編碼前的實(shí)體主體的類型。

        添加其他功能

        至此,已經(jīng)完成了基本功能中列出的幾個步驟,但依然有很多需要改進(jìn)的地方,比如如果用戶輸入的url對應(yīng)的是磁盤上的一個目錄怎么辦?還有,現(xiàn)在對于同一個文件(從未更改過)的多次請求,服務(wù)端都是勤勤懇懇地一遍遍地發(fā)送回同樣的文件,這些冗余的數(shù)據(jù)傳輸,既消耗了帶寬,也給服務(wù)器添加了負(fù)擔(dān)。另外,服務(wù)器如果在發(fā)送內(nèi)容之前能對其進(jìn)行壓縮,也有助于減少傳輸時間。

        讀取文件目錄

        現(xiàn)階段,用url: localhost:9527/testfolder去訪問一個指定root文件夾下真實(shí)存在的testfolder的文件夾,服務(wù)端會報(bào)錯:

        Error: EISDIR: illegal operation on a directory, read

        要增添對目錄訪問的支持,我們重新整理下響應(yīng)的步驟:

        1.請求抵達(dá)時,首先判斷url是否有尾部斜杠

        2.如果有尾部斜杠,認(rèn)為用戶請求的是目錄

      5. 如果目錄存在
      6. 如果目錄下存在默認(rèn)頁(如index.html),發(fā)送默認(rèn)頁
      7. 如果不存在默認(rèn)頁,發(fā)送目錄下內(nèi)容列表
      8. 如果目錄不存在,返回404
      9. 3.如果沒有尾部斜杠,認(rèn)為用戶請求的是文件

      10. 如果文件存在,發(fā)送文件
      11. 如果文件不存在,判斷同名的目錄是否存在
      12. 如果存在該目錄,返回301,并在原url上添加上/作為要轉(zhuǎn)到的location
      13. 如果不存在該目錄,返回404
      14. 我們需要重寫一下routeHandler內(nèi)的邏輯:

        routeHandler(pathName, req, res) {
         fs.stat(pathName, (err, stat) => {
         if (!err) {
         const requestedPath = url.parse(req.url).pathname;
         if (hasTrailingSlash(requestedPath) && stat.isDirectory()) {
         this.respondDirectory(pathName, req, res);
         } else if (stat.isDirectory()) {
         this.respondRedirect(req, res);
         } else {
         this.respondFile(pathName, req, res);
         }
         } else {
         this.respondNotFound(req, res);
         }
         });
         }

        繼續(xù)補(bǔ)充respondRedirect方法:

         respondRedirect(req, res) {
         const location = req.url + '/';
         res.writeHead(301, {
         'Location': location,
         'Content-Type': 'text/html'
         });
         res.end(`Redirecting to <a href='${location}'>${location}</a>`);
         }

        瀏覽器收到301響應(yīng)時,會根據(jù)頭部指定的location字段值,向服務(wù)器發(fā)出一個新的請求。

        繼續(xù)補(bǔ)充respondDirectory方法:

         respondDirectory(pathName, req, res) {
         const indexPagePath = path.join(pathName, this.indexPage);
         if (fs.existsSync(indexPagePath)) {
         this.respondFile(indexPagePath, req, res);
         } else {
         fs.readdir(pathName, (err, files) => {
         if (err) {
         res.writeHead(500);
         return res.end(err);
         }
         const requestPath = url.parse(req.url).pathname;
         let content = `<h1>Index of ${requestPath}</h1>`;
         files.forEach(file => {
         let itemLink = path.join(requestPath,file);
         const stat = fs.statSync(path.join(pathName, file));
         if (stat && stat.isDirectory()) {
         itemLink = path.join(itemLink, '/');
         } 
         content += `<p><a href='${itemLink}'>${file}</a></p>`;
         });
         res.writeHead(200, {
         'Content-Type': 'text/html'
         });
         res.end(content);
         });
         }
         }

        當(dāng)需要返回目錄列表時,遍歷所有內(nèi)容,并為每項(xiàng)創(chuàng)建一個link,作為返回文檔的一部分。需要注意的是,對于子目錄的href,額外添加一個尾部斜杠,這樣可以避免訪問子目錄時的又一次重定向。

        在瀏覽器中測試一下,輸入localhost:9527/testfolder,指定的root目錄下并沒有名為testfolder的文件,卻存在同名目錄,因此第一次會收到重定向響應(yīng),并發(fā)起一個對目錄的新請求。

        緩存支持

        為了減少數(shù)據(jù)傳輸,減少請求數(shù),繼續(xù)添加緩存支持。首先梳理一下緩存的處理流程:

        1.如果是第一次訪問,請求報(bào)文首部不會包含相關(guān)字段,服務(wù)端在發(fā)送文件前做如下處理:

      15. 如服務(wù)器支持ETag,設(shè)置ETag頭
      16. 如服務(wù)器支持Last-Modified,設(shè)置Last-Modified頭
      17. 設(shè)置Expires頭
      18. 設(shè)置Cache-Control頭(設(shè)置其max-age值)
      19. 瀏覽器收到響應(yīng)后會存下這些標(biāo)記,并在下次請求時帶上與ETag對應(yīng)的請求首部If-None-Match或與Last-Modified對應(yīng)的請求首部If-Modified-Since。
      20. 2.如果是重復(fù)的請求:

        瀏覽器判斷緩存是否過期(通過Cache-Control和Expires確定)

        如果未過期,直接使用緩存內(nèi)容,也就是強(qiáng)緩存命中,并不會產(chǎn)生新的請求

        如果已過期,會發(fā)起新的請求,并且請求會帶上If-None-Match或If-Modified-Since,或者兼具兩者

        服務(wù)器收到請求,進(jìn)行緩存的新鮮度再驗(yàn)證:
        首先檢查請求是否有If-None-Match首部,沒有則繼續(xù)下一步,有則將其值與文檔的最新ETag匹配,失敗則認(rèn)為緩存不新鮮,成功則繼續(xù)下一步

        接著檢查請求是否有If-Modified-Since首部,沒有則保留上一步驗(yàn)證結(jié)果,有則將其值與文檔最新修改時間比較驗(yàn)證,失敗則認(rèn)為緩存不新鮮,成功則認(rèn)為緩存新鮮

        當(dāng)兩個首部皆不存在或者驗(yàn)證結(jié)果是不新鮮時,發(fā)送200及最新文件,并在首部更新新鮮度。

        當(dāng)驗(yàn)證結(jié)果是緩存仍然新鮮時(也就是弱緩存命中),不需發(fā)送文件,僅發(fā)送304,并在首部更新新鮮度

        為了能啟用或關(guān)閉某種驗(yàn)證機(jī)制,我們在配置文件里增添如下配置項(xiàng):

        default.json:

        {
         ...
         "cacheControl": true,
         "expires": true,
         "etag": true,
         "lastModified": true,
         "maxAge": 5
        }

        這里為了能測試到緩存過期,將過期時間設(shè)成了非常小的5秒。

        在StaticServer類中接收這些配置:

        class StaticServer {
         constructor() {
         ...
         this.enableCacheControl = config.cacheControl;
         this.enableExpires = config.expires;
         this.enableETag = config.etag;
         this.enableLastModified = config.lastModified;
         this.maxAge = config.maxAge;
         }

        現(xiàn)在,我們要在原來的respondFile前橫加一杠,增加是要返回304還是200的邏輯。

         respond(pathName, req, res) {
         fs.stat(pathName, (err, stat) => {
         if (err) return respondError(err, res);
         this.setFreshHeaders(stat, res);
         if (this.isFresh(req.headers, res._headers)) {
         this.responseNotModified(res);
         } else {
         this.responseFile(pathName, res);
         }
         });
         }

        準(zhǔn)備返回文件前,根據(jù)配置,添加緩存相關(guān)的響應(yīng)首部。

         generateETag(stat) {
         const mtime = stat.mtime.getTime().toString(16);
         const size = stat.size.toString(16);
         return `W/"${size}-${mtime}"`;
         }
         setFreshHeaders(stat, res) {
         const lastModified = stat.mtime.toUTCString();
         if (this.enableExpires) {
         const expireTime = (new Date(Date.now() + this.maxAge * 1000)).toUTCString();
         res.setHeader('Expires', expireTime);
         }
         if (this.enableCacheControl) {
         res.setHeader('Cache-Control', `public, max-age=${this.maxAge}`);
         }
         if (this.enableLastModified) {
         res.setHeader('Last-Modified', lastModified);
         }
         if (this.enableETag) {
         res.setHeader('ETag', this.generateETag(stat));
         }
         }

        需要注意的是,上面使用了ETag弱驗(yàn)證器,并不能保證緩存文件與服務(wù)器上的文件是完全一樣的。關(guān)于強(qiáng)驗(yàn)證器如何實(shí)現(xiàn),可以參考etag包的源碼。

        下面是如何判斷緩存是否仍然新鮮:

         isFresh(reqHeaders, resHeaders) {
         const noneMatch = reqHeaders['if-none-match'];
         const lastModified = reqHeaders['if-modified-since'];
         if (!(noneMatch || lastModified)) return false;
         if(noneMatch && (noneMatch !== resHeaders['etag'])) return false;
         if(lastModified && lastModified !== resHeaders['last-modified']) return false;
         return true;
         }

        需要注意的是,http首部字段名是不區(qū)分大小寫的(但http method應(yīng)該大寫),所以平常在瀏覽器中會看到大寫或小寫的首部字段。

        但是node的http模塊將首部字段都轉(zhuǎn)成了小寫,這樣在代碼中使用起來更方便些。所以訪問header要用小寫,如reqHeaders['if-none-match']。不過,仍然可以用req.rawreq.rawHeaders來訪問原h(huán)eaders,它是一個[name1, value1, name2, value2, ...]形式的數(shù)組。

        現(xiàn)在來測試一下,因?yàn)樵O(shè)置的緩存有效時間是極小的5s,所以強(qiáng)緩存幾乎不會命中,所以第二次訪問文件會發(fā)出新的請求,因?yàn)榉?wù)端文件并沒做什么改變,所以會返回304。

        現(xiàn)在來修改一下請求的這張圖片,比如修改一下size,目的是讓服務(wù)端的再驗(yàn)證失敗,因而必須給客戶端發(fā)送200和最新的文件。

        接下來把緩存有效時間改大一些,比如10分鐘,那么在10分鐘之內(nèi)的重復(fù)請求,都會命中強(qiáng)緩存,瀏覽器不會向服務(wù)端發(fā)起新的請求(但network依然能觀察到這條請求)。

        內(nèi)容編碼

        服務(wù)器在發(fā)送很大的文檔之前,對其進(jìn)行壓縮,可以節(jié)省傳輸用時。其過程是:

        瀏覽器在訪問網(wǎng)站時,默認(rèn)會攜帶Accept-Encoding頭

        服務(wù)器在收到請求后,如果發(fā)現(xiàn)存在Accept-Encoding請求頭,并且支持該文件類型的壓縮,壓縮響應(yīng)的實(shí)體主體(并不壓縮頭部),并附上Content-Encoding首部

        瀏覽器收到響應(yīng),如果發(fā)現(xiàn)有Content-Encoding首部,按其值指定的格式解壓報(bào)文

        對于圖片這類已經(jīng)經(jīng)過高度壓縮的文件,無需再額外壓縮。因此,我們需要配置一個字段,指明需要針對哪些類型的文件進(jìn)行壓縮。

        default.json

        {
         ...
         "zipMatch": "^\\.(css|js|html)$"
        }
        static-server.js
         constructor() {
         ...
         this.zipMatch = new RegExp(config.zipMatch);
         }

        用zlib模塊來實(shí)現(xiàn)流壓縮:

        compressHandler(readStream, req, res) { const acceptEncoding = req.headers['accept-encoding']; if (!acceptEncoding || !acceptEncoding.match(/\b(gzip|deflate)\b/)) { return readStream; } else if (acceptEncoding.match(/\bgzip\b/)) { res.setHeader('Content-Encoding', 'gzip'); return readStream.pipe(zlib.createGzip()); } else if (acceptEncoding.match(/\bdeflate\b/)) { res.setHeader('Content-Encoding', 'deflate'); return readStream.pipe(zlib.createDeflate()); } }

        因?yàn)榕渲昧藞D片不需壓縮,在瀏覽器中測試會發(fā)現(xiàn)圖片請求的響應(yīng)中沒有Content-Encoding頭。

        范圍請求

        最后一步,使服務(wù)器支持范圍請求,允許客戶端只請求文檔的一部分。其流程是:

      21. 客戶端向服務(wù)端發(fā)起請求
      22. 服務(wù)端響應(yīng),附上Accept-Ranges頭(值表示表示范圍的單位,通常是“bytes”),告訴客戶端其接受范圍請求
      23. 客戶端發(fā)送新的請求,附上Ranges頭,告訴服務(wù)端請求的是一個范圍
      24. 服務(wù)端收到范圍請求,分情況響應(yīng):
      25. 范圍有效,服務(wù)端返回206 Partial Content,發(fā)送指定范圍內(nèi)內(nèi)容,并在Content-Range頭中指定該范圍

        范圍無效,服務(wù)端返回416 Requested Range Not Satisfiable,并在Content-Range中指明可接受范圍

        請求中的Ranges頭格式為(這里不考慮多范圍請求了):

        Ranges: bytes=[start]-[end]

        其中 start 和 end 并不是必須同時具有:

        如果 end 省略,服務(wù)器應(yīng)返回從 start 位置開始之后的所有字節(jié)

        如果 start 省略,end 值指的就是服務(wù)器該返回最后多少個字節(jié)

        如果均未省略,則服務(wù)器返回 start 和 end 之間的字節(jié)

        響應(yīng)中的Content-Range頭有兩種格式:

        當(dāng)范圍有效返回 206 時:

        Content-Range: bytes (start)-(end)/(total)

        當(dāng)范圍無效返回 416 時:

        Content-Range: bytes */(total)

        添加函數(shù)處理范圍請求:

         rangeHandler(pathName, rangeText, totalSize, res) {
         const range = this.getRange(rangeText, totalSize);
         if (range.start > totalSize || range.end > totalSize || range.start > range.end) {
         res.statusCode = 416;
         res.setHeader('Content-Range', `bytes */${totalSize}`);
         res.end();
         return null;
         } else {
         res.statusCode = 206;
         res.setHeader('Content-Range', `bytes ${range.start}-${range.end}/${totalSize}`);
         return fs.createReadStream(pathName, { start: range.start, end: range.end });
         }
         }

        用 Postman來測試一下。在指定的root文件夾下創(chuàng)建一個測試文件:

        testfile.js

        This is a test sentence.

        請求返回前六個字節(jié) ”This “ 返回 206:

        請求一個無效范圍返回416:

        讀取命令行參數(shù)

        至此,已經(jīng)完成了靜態(tài)服務(wù)器的基本功能。但是每一次需要修改配置,都必須修改default.json文件,非常不方便,如果能接受命令行參數(shù)就好了,可以借助 yargs 模塊來完成。

        var options = require( "yargs" )
         .option( "p", { alias: "port", describe: "Port number", type: "number" } )
         .option( "r", { alias: "root", describe: "Static resource directory", type: "string" } )
         .option( "i", { alias: "index", describe: "Default page", type: "string" } )
         .option( "c", { alias: "cachecontrol", default: true, describe: "Use Cache-Control", type: "boolean" } )
         .option( "e", { alias: "expires", default: true, describe: "Use Expires", type: "boolean" } )
         .option( "t", { alias: "etag", default: true, describe: "Use ETag", type: "boolean" } )
         .option( "l", { alias: "lastmodified", default: true, describe: "Use Last-Modified", type: "boolean" } )
         .option( "m", { alias: "maxage", describe: "Time a file should be cached for", type: "number" } )
         .help()
         .alias( "?", "help" )
         .argv;

        瞅瞅 help 命令會輸出啥:

        這樣就可以在命令行傳遞端口、默認(rèn)頁等:

        node app.js -p 8888 -i main.html

        總結(jié)

        以上所述是小編給大家介紹的使用Node.js搭建靜態(tài)資源服務(wù)詳細(xì)教程,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

        聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        使用Node.js搭建靜態(tài)資源服務(wù)詳細(xì)教程

        使用Node.js搭建靜態(tài)資源服務(wù)詳細(xì)教程:對于Node.js新手,搭建一個靜態(tài)資源服務(wù)器是個不錯的鍛煉,從最簡單的返回文件或錯誤開始,漸進(jìn)增強(qiáng),還可以逐步加深對http的理解。那就開始吧,讓我們的雙手沾滿網(wǎng)絡(luò)請求! Note: 當(dāng)然在項(xiàng)目中如果有使用express框架,用express.static一行代碼就可
        推薦度:
        標(biāo)簽: 創(chuàng)建 js 搭建
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲日本一线产区和二线| 3344在线看片免费| 免费亚洲视频在线观看| 香蕉视频在线免费看| 国产91色综合久久免费分享| 四虎影视精品永久免费| 亚洲黄色在线播放| 一级毛片在线免费视频| 成人免费无码大片a毛片软件| 亚洲精品无码午夜福利中文字幕 | 亚洲无码精品浪潮| 亚洲福利电影一区二区?| 国产免费播放一区二区| 国产亚洲成av片在线观看| 免费国产成人α片| 国产精品亚洲二区在线观看| xxxxx做受大片视频免费| 久久亚洲国产中v天仙www | 黄桃AV无码免费一区二区三区 | 香蕉视频在线免费看| 亚洲Av无码专区国产乱码DVD| 色噜噜狠狠色综合免费视频| 最近最新中文字幕完整版免费高清| 亚洲依依成人亚洲社区| 精品国产污污免费网站aⅴ| 亚洲中文字幕无码久久2020 | 日本免费v片一二三区| 亚洲色中文字幕在线播放| 免费jjzz在在线播放国产| 亚洲a∨国产av综合av下载 | xxxxx做受大片在线观看免费| 久久精品7亚洲午夜a| 粉色视频成年免费人15次| 久久精品国产亚洲一区二区三区| 激情婷婷成人亚洲综合| 日韩一级免费视频| 国产午夜无码精品免费看| 亚洲中文字幕久久精品无码VA| 亚洲国产激情一区二区三区| 黄视频在线观看免费| 亚洲午夜精品在线|