欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時(shí)間:2017年08月02日 08:44:18   投稿:mrr  
這篇文章主要介紹了使用Node.js搭建靜態(tài)資源服務(wù)器,需要的朋友可以參考下

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

Note:

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

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

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

基本功能

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

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

6.等待來(lái)自客戶(hù)端的下一個(gè)請(qǐng)求

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

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

創(chuàng)建一個(gè)nodejs-static-webserver目錄,在目錄內(nèi)運(yùn)行npm init初始化一個(gè)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)配置,比如端口號(hào)、靜態(tài)文件目錄(root)、默認(rèn)頁(yè)(indexPage)等。當(dāng)這樣的一個(gè)請(qǐng)求http://localhost:9527/myfiles/抵達(dá)時(shí). 如果根據(jù)root映射后得到的目錄內(nèi)有index.html,根據(jù)我們的默認(rèn)配置,就會(huì)給客戶(hù)端發(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;

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

app.js

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


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

在目錄下啟動(dòng)程序會(huì)看到成功啟動(dòng)的log:

> node app.js
Server started on port 9527

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

路由處理

之前我們對(duì)任何請(qǐng)求都只是向客戶(hù)端返回文件位置而已,現(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來(lái)處理文件發(fā)送。

讀取靜態(tài)文件

讀取文件之前,用fs.stat檢測(cè)文件是否存在,如果文件不存在,回調(diào)函數(shù)會(huì)接收到錯(cuò)誤,發(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)楹笳邥?huì)在得到完整文件內(nèi)容之前將其先讀到內(nèi)存里。這樣萬(wàn)一文件很大,再遇上多個(gè)請(qǐng)求同時(shí)訪問(wèn),readFile就承受不來(lái)了。使用文件可讀流,服務(wù)端不用等到數(shù)據(jù)完全加載到內(nèi)存再發(fā)回給客戶(hù)端,而是一邊讀一邊發(fā)送分塊響應(yīng)。這時(shí)響應(yīng)里會(huì)包含如下響應(yīng)頭:

Transfer-Encoding:chunked

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

MIME支持

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

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

在根目錄下創(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
};

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

接著在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)行程序,會(huì)看到圖片可以在瀏覽器中正常顯示了。

Note:

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

添加其他功能

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

讀取文件目錄

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

Error: EISDIR: illegal operation on a directory, read

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

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

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

  • 如果目錄存在
  • 如果目錄下存在默認(rèn)頁(yè)(如index.html),發(fā)送默認(rèn)頁(yè)
  • 如果不存在默認(rèn)頁(yè),發(fā)送目錄下內(nèi)容列表
  • 如果目錄不存在,返回404

3.如果沒(méi)有尾部斜杠,認(rèn)為用戶(hù)請(qǐng)求的是文件

  • 如果文件存在,發(fā)送文件
  • 如果文件不存在,判斷同名的目錄是否存在
  • 如果存在該目錄,返回301,并在原url上添加上/作為要轉(zhuǎn)到的location
  • 如果不存在該目錄,返回404

我們需要重寫(xiě)一下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)時(shí),會(huì)根據(jù)頭部指定的location字段值,向服務(wù)器發(fā)出一個(gè)新的請(qǐng)求。

繼續(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)需要返回目錄列表時(shí),遍歷所有內(nèi)容,并為每項(xiàng)創(chuàng)建一個(gè)link,作為返回文檔的一部分。需要注意的是,對(duì)于子目錄的href,額外添加一個(gè)尾部斜杠,這樣可以避免訪問(wèn)子目錄時(shí)的又一次重定向。

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

緩存支持

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

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

  • 如服務(wù)器支持ETag,設(shè)置ETag頭
  • 如服務(wù)器支持Last-Modified,設(shè)置Last-Modified頭
  • 設(shè)置Expires頭
  • 設(shè)置Cache-Control頭(設(shè)置其max-age值)
  • 瀏覽器收到響應(yīng)后會(huì)存下這些標(biāo)記,并在下次請(qǐng)求時(shí)帶上與ETag對(duì)應(yīng)的請(qǐng)求首部If-None-Match或與Last-Modified對(duì)應(yīng)的請(qǐng)求首部If-Modified-Since。

2.如果是重復(fù)的請(qǐng)求:

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

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

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

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

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

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

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

為了能啟用或關(guān)閉某種驗(yàn)證機(jī)制,我們?cè)谂渲梦募镌鎏砣缦屡渲庙?xiàng):

default.json:

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

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

在StaticServer類(lèi)中接收這些配置:

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)在,我們要在原來(lái)的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ū)分大小寫(xiě)的(但http method應(yīng)該大寫(xiě)),所以平常在瀏覽器中會(huì)看到大寫(xiě)或小寫(xiě)的首部字段。

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

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

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

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

內(nèi)容編碼

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

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

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

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

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

default.json

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

用zlib模塊來(lái)實(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片不需壓縮,在瀏覽器中測(cè)試會(huì)發(fā)現(xiàn)圖片請(qǐng)求的響應(yīng)中沒(méi)有Content-Encoding頭。

范圍請(qǐng)求

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

  • 客戶(hù)端向服務(wù)端發(fā)起請(qǐng)求
  • 服務(wù)端響應(yīng),附上Accept-Ranges頭(值表示表示范圍的單位,通常是“bytes”),告訴客戶(hù)端其接受范圍請(qǐng)求
  • 客戶(hù)端發(fā)送新的請(qǐng)求,附上Ranges頭,告訴服務(wù)端請(qǐng)求的是一個(gè)范圍
  • 服務(wù)端收到范圍請(qǐng)求,分情況響應(yīng):

范圍有效,服務(wù)端返回206 Partial Content,發(fā)送指定范圍內(nèi)內(nèi)容,并在Content-Range頭中指定該范圍

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

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

Ranges: bytes=[start]-[end]

其中 start 和 end 并不是必須同時(shí)具有:

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

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

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

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

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

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

當(dāng)范圍無(wú)效返回 416 時(shí):

Content-Range: bytes */(total)

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

 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來(lái)測(cè)試一下。在指定的root文件夾下創(chuàng)建一個(gè)測(cè)試文件:

testfile.js

This is a test sentence.

請(qǐng)求返回前六個(gè)字節(jié) ”This “ 返回 206:

請(qǐng)求一個(gè)無(wú)效范圍返回416:

讀取命令行參數(shù)

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

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 命令會(huì)輸出啥:

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

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

總結(jié)

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

相關(guān)文章

  • node.js中的http.response.getHeader方法使用說(shuō)明

    node.js中的http.response.getHeader方法使用說(shuō)明

    這篇文章主要介紹了node.js中的http.response.getHeader方法使用說(shuō)明,本文介紹了http.response.getHeader的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下
    2014-12-12
  • yarn?命令死循環(huán)問(wèn)題分析解決

    yarn?命令死循環(huán)問(wèn)題分析解決

    這篇文章主要為大家介紹了yarn?命令死循環(huán)問(wèn)題分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • NodeJS制作爬蟲(chóng)全過(guò)程

    NodeJS制作爬蟲(chóng)全過(guò)程

    這篇文章主要介紹了NodeJS制作爬蟲(chóng)的全過(guò)程,包括項(xiàng)目建立,目標(biāo)網(wǎng)站分析、使用superagent獲取源數(shù)據(jù)、使用cheerio解析、使用eventproxy來(lái)并發(fā)抓取每個(gè)主題的內(nèi)容等方面,有需要的小伙伴參考下吧。
    2014-12-12
  • node.js微信公眾平臺(tái)開(kāi)發(fā)教程

    node.js微信公眾平臺(tái)開(kāi)發(fā)教程

    這篇文章主要為大家分享了node.js微信公眾平臺(tái)開(kāi)發(fā)教程,如何進(jìn)行微信開(kāi)發(fā),感興趣的小伙伴們可以參考一下
    2016-03-03
  • 分享五個(gè)Node.js開(kāi)發(fā)的優(yōu)秀實(shí)踐?

    分享五個(gè)Node.js開(kāi)發(fā)的優(yōu)秀實(shí)踐?

    這篇文章主要介紹了分享五個(gè)Node.js開(kāi)發(fā)的優(yōu)秀實(shí)踐,文章圍繞主題展開(kāi)詳細(xì)的分享內(nèi)容,需要的小伙伴可以參考一下,希望對(duì)你的工作有所幫助
    2022-04-04
  • Node.js實(shí)現(xiàn)http請(qǐng)求服務(wù)與Mysql數(shù)據(jù)庫(kù)操作方法詳解

    Node.js實(shí)現(xiàn)http請(qǐng)求服務(wù)與Mysql數(shù)據(jù)庫(kù)操作方法詳解

    這篇文章主要介紹了Node.js實(shí)現(xiàn)http請(qǐng)求服務(wù)與Mysql數(shù)據(jù)庫(kù)操作方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-10-10
  • NodeJS模塊Buffer原理及使用方法解析

    NodeJS模塊Buffer原理及使用方法解析

    這篇文章主要介紹了NodeJS模塊Buffer原理及使用方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • nestjs搭建HTTP與WebSocket服務(wù)詳細(xì)過(guò)程

    nestjs搭建HTTP與WebSocket服務(wù)詳細(xì)過(guò)程

    這篇文章主要介紹了nestjs搭建HTTP與WebSocket服務(wù)詳細(xì)過(guò)程的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • nodejs個(gè)人博客開(kāi)發(fā)第二步 入口文件

    nodejs個(gè)人博客開(kāi)發(fā)第二步 入口文件

    這篇文章主要為大家詳細(xì)介紹了nodejs個(gè)人博客開(kāi)發(fā)的入口文件,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Node.js的基本知識(shí)簡(jiǎn)單匯總

    Node.js的基本知識(shí)簡(jiǎn)單匯總

    本文主要給大家簡(jiǎn)單介紹了Node.js的基本知識(shí),包括概念、特點(diǎn)、歷史、案例的相關(guān)資料,需要的朋友可以參考下
    2016-09-09

最新評(píng)論