標籤:

node.js構建靜態伺服器

**利用node.js構建靜態伺服器,讓你理解底層的伺服器是怎樣實現的,讓你對伺服器有一個更好的認識。**

實現了如下功能:

- 讀取靜態文件

- MIME類型支持

- 緩存支持/控制

- 支持gzip壓縮

- 只能訪問指定目錄, 不能訪問指定目錄的上級目錄,保證安全

- 訪問目錄可以自動尋找下面的index.html文件

- Range支持,斷點續傳

- 發布為可執行命令並可以後台運行,可以通過npm install -g安裝

### 讀取靜態文件

```

let { promisify, inspect } = require(util);

let stat = promisify(fs.stat);

let readdir = promisify(fs.readdir);

let statObj = await stat(filepath);

if (statObj.isDirectory()) {//如果是目錄的話,應該顯示目錄 下面的文件列表

let files = await readdir(filepath);

files = files.map(file => ({

name: file,

url: path.join(pathname, file)

}));

let html = this.list({

title: pathname,

files

});

res.setHeader(Content-Type, text/html);

res.end(html);

} else {

this.sendFile(req, res, filepath, statObj);

}

sendFile(req, res, filepath, statObj) {

if (this.handleCache(req, res, filepath, statObj)) return; //如果走緩存,則直接返回

res.setHeader(Content-Type, mime.getType(filepath) + ;charset=utf-8);// .jpg

let encoding = this.getEncoding(req, res);

let rs = this.getStream(req, res, filepath, statObj);

if (encoding) {

rs.pipe(encoding).pipe(res);

} else {

rs.pipe(res);

}

}

getStream(req, res, filepath, statObj) {

let start = 0;

let end = statObj.size - 1;

let range = req.headers[range];

return fs.createReadStream(filepath, {

start, end

});

}

```

### MiMe類型檢測

這個很簡單,對應的代碼是:

```

let mime = require(mime);

res.setHeader(Content-Type, mime.getType(filepath) + ;charset=utf-8);// .jpg

```

### 緩存支持/控制, 對應的代碼是:

```

if (this.handleCache(req, res, filepath, statObj)) return; //如果走緩存,則直接返回

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);

res.setHeader(Expires, new Date(Date.now() + 30 * 1000).toGMTString());

let etag = statObj.size;

let lastModified = statObj.ctime.toGMTString();

res.setHeader(ETag, etag);

res.setHeader(Last-Modified, lastModified);

if (isNoneMatch && isNoneMatch != etag) {

return fasle;

}

if (ifModifiedSince && ifModifiedSince != lastModified) {

return fasle;

}

if (isNoneMatch || ifModifiedSince) {

res.writeHead(304);

res.end();

return true;

} else {

return false;

}

}

```

### 支持gzip壓縮

```

getEncoding(req, res) {

//Accept-Encoding:gzip, deflate

let acceptEncoding = req.headers[accept-encoding];

if (/gzip/.test(acceptEncoding)) {

res.setHeader(Content-Encoding, gzip);

return zlib.createGzip();

} else if (/deflate/.test(acceptEncoding)) {

res.setHeader(Content-Encoding, deflate);

return zlib.createDeflate();

} else {

return null;

}

}

if (encoding) {

rs.pipe(encoding).pipe(res);

} else {

rs.pipe(res);

}

```

### 只能訪問指定目錄, 不能訪問指定目錄的上級目錄,保證安全

假如一個同學用瀏覽器訪問http://localhost:8000/../xxx.js 怎麼辦捏?瀏覽器會自動幹掉那兩個作為父路徑的點的。瀏覽器會把這個路徑組裝成http://localhost:8000/xxx.js

但是curl命令可以:

curl -i localhost:8000/../xxx.j

那麼怎麼辦呢?

```

var realPath = path.join("assets", path.normalize(pathname.replace(/../g, "")));

```

強制替換即可。

### Range支持,斷點續傳

```

getStream(req, res, filepath, statObj) {

let start = 0;

let end = statObj.size - 1;

let range = req.headers[range];

if (range) {

res.setHeader(Accept-Range, bytes);

res.statusCode = 206;//返回整個內容的一塊

let result = range.match(/bytes=(d*)-(d*)/);

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

});

}

```

*** 發布為可執行命令並可以後台運行,可以通過npm install -g安裝

在package中添加:

```

"bin": {

"studytest": "bin/www"

},

```

執行 npm link

這個時候其實就可以全局執行了

最後發布

- npm adduser

- npm publish


推薦閱讀:

js中什麼技術能合併多個前端請求,並生成一個json文件發送?
node.js 已經淡出眾多開發者的視野了嗎?
Promise then中回調為什麼是非同步執行?
nodejs中,zlib.gzip系列純cpu計算函數為什麼會有非同步版本?
ECMAScript 6 的模塊相比 CommonJS 的require (...)有什麼優點?

TAG:Nodejs | 伺服器 |