中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

使用Node.js怎么實現一個靜態資源服務器

發布時間:2021-05-20 17:07:40 來源:億速云 閱讀:183 作者:Leah 欄目:web開發

這篇文章給大家介紹使用Node.js怎么實現一個靜態資源服務器,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

step1 新建項目

目錄結構如下:

使用Node.js怎么實現一個靜態資源服務器

bin文件夾存放我們的執行代碼,web作為一個測試文件夾,里面放了些網頁。

step2 碼碼

step2.1 雛形

靜態資源服務器,通俗講就是我們在瀏覽器地址欄輸入形如“http://域名/test/index.html”的一個地址,服務器從根目錄下的對應文件夾找到index.html,讀出文件內容并返回給瀏覽器,瀏覽器渲染給用戶。

const http = require("http");
const url = require("url");
const fs = require("fs");
const path = require("path");

const item = (name, parentPath) => {
  let path = parentPath = `${parentPath}/${name}`.slice(1);
  return `<div><a href="${path}" rel="external nofollow" >${name}</a></div>`;
}

const list = (arr, parentPath) => {
  return arr.map(name => item(name, parentPath)).join("");
}

const server = http.createServer((req, res) => {
  let _path = url.parse(req.url).pathname;//去掉search
  let parentPath = _path;
  _path = path.join(__dirname, _path);
  try {
    //拿到路徑所對應的文件描述對象
    let stats = fs.statSync(_path);
    if (stats.isFile()) {
      //是文件,返回文件內容
      let file = fs.readFileSync(_path);
      res.end(file);
    } else if (stats.isDirectory()) {
      //是目錄,返回目錄列表,讓用戶可以繼續點擊
      let dirArray = fs.readdirSync(_path);
      res.end(list(dirArray, parentPath));
    } else {
      res.end();
    }
  } catch (err) {
    res.writeHead(404, "Not Found");
    res.end();
  }
});

const port = 2234;
const hostname = "127.0.0.1";
server.listen(port, hostname, () => {
  console.log(`server is running on http://${hostname}:${port}`);
});

以上這段code就是我們的核心代碼了,已經實現了核心功能,本地運行即可看到返回了文件目錄,點擊文件名便可瀏覽對應的網頁、圖片、文本啦。

step2.2 優化

功能實現了,但是我們可以在某些方面做做優化,提升實用性,順便多學習幾個api(裝逼技巧)。

1. stream

我們目前讀取文件返回給瀏覽器的操作是通過readFile一次性讀出來,一次性返回,這樣當然可以實現功能,但我們有更好的方式——用stream(流)進行IO操作。stream并不是node.js獨有的概念,而是操作系統最基本的一種操作形式,所以理論上講,任何一門server端語言都實現了stream的API。

為什么講用stream是一種更好的方式?因為一次性讀取、操作大文件,內存和網絡是吃不消的,尤其在用戶訪問量比較大的情況下更為明顯;而借助stream可以讓數據流動起來,一點一點操作,從而提升性能。代碼修改如下:

if (stats.isFile()) {
  //是文件,返回文件內容
  //在createServer時傳入的回調函數被添加到了"request"事件上,回調函數的兩個形參req和res
  //分別為http.IncomingMessage對象和http.ServerResponse對象
  //并且它們都實現了流接口
  let readStream = fs.createReadStream(_path);
  readStream.pipe(res);
}

編碼實現非常簡單,在需要返回文件內容時,我們創建了一個可讀流,并把它直接導向了res對象。

2. gzip壓縮

gzip壓縮帶來的性能(用戶訪問體驗)提升是非常明顯的,尤其在當下spa應用大行其道的時代,開啟gzip壓縮,可以大幅減小js、css等文件資源的體積,提升用戶訪問速度。作為一個靜態資源服務器,我們當然要加上這個功能。

node中有一個zlib的模塊,提供了很多壓縮相關的api,我們就用它來實現:

const zlib = require("zlib");

if (stats.isFile()) {
  //是文件,返回文件內容

  res.setHeader("content-encoding", "gzip");
  
  const gzip = zlib.createGzip();
  let readStream = fs.createReadStream(_path);
  readStream.pipe(gzip).pipe(res);
}

有了stream的使用經驗,我們再看這段代碼的時候就好理解多了。把文件流先導向gzip對象,再導向res對象。此外,使用gzip壓縮的時候還需要注意一點:需要把響應頭里的content-encoding設置為gzip。否則瀏覽器會把一堆亂碼展示出來。

3. http緩存

緩存這個東西讓人又愛又恨,用得好,可以提升用戶體驗,減輕服務器壓力;用得不好,可能就會面臨各種各樣奇奇怪怪的問題。一般來講瀏覽器http緩存分為強緩存(非驗證性緩存)和協商緩存(驗證性緩存)。

什么叫強緩存呢?強緩存是由cache-control和expires兩個首部字段控制的,現在一般用cache-control。比如我們設置了cache-control: max-age=31536000的響應頭,就是告訴瀏覽器這個資源有一年的緩存期,一年內不用向服務端發送請求,直接從緩存中讀取資源。

而協商性緩存是使用if-modified-since/last-modified、if-none-match/etag等首部字段,配合強緩存,在強緩存沒有命中(或告知瀏覽器no-cache)的時候,向服務器發送請求,確認資源的有效性,決定從緩存中讀取或是返回新的資源。

有了以上概念,我們便可以制定我們的緩存策略:

if (stats.isFile()) {
  //是文件,返回文件內容
  
  //增加判斷文件是否有改動,沒有改動返回304的邏輯
  
  //從請求頭獲取modified時間
  let IfModifiedSince = req.headers["if-modified-since"];
  //獲取文件的修改日期——時間戳格式
  let mtime = stats.mtime;
  //如果服務器上的文件修改時間小于等于請求頭攜帶的修改時間,則認定文件沒有變化
  if (IfModifiedSince && mtime <= new Date(IfModifiedSince).getTime()) {
    //返回304
    res.writeHead(304, "not modify");
    return res.end();
  }
  //第一次請求或文件被修改后,返回給客戶端新的修改時間
  res.setHeader("last-modified", new Date(mtime).toString());
  res.setHeader("content-encoding", "gzip");
  let reg = /\.html$/;
  //不同的文件類型設置不同的cache-control
  if (reg.test(_path)) {
    //我們對html文件執行每次必須向服務器驗證資源有效性的策略
    res.setHeader("cache-control", "no-cache");
  } else {
    //我們對其余的靜態資源文件采取強緩存策略,一個月內無需向服務器索取
    res.setHeader("cache-control", `max-age=${1 * 60 * 60 * 24 * 30}`);
  }
  
  //執行gzip壓縮
  const gzip = zlib.createGzip();
  let readStream = fs.createReadStream(_path);
  readStream.pipe(gzip).pipe(res);
}

這樣一套緩存策略在現代前端項目體系下還是比較合適的,尤其是對于spa應用來講。我們希望index.html能夠保證每次向服務器驗證是否有更新,而其余的文件統一本地緩存一個月(自己定);通過webpack打包或其他工程化方式構建之后,js、css內容如果發生變化,文件名相應更新,index.html插入的manifest(或script鏈接、link鏈接等)清單會更新,保證用戶能夠實時得到最新的資源。

當然,緩存之路千萬條,適合業務才重要,大家可以靈活制定。

4. 命令行參數

作為一個在命令行執行的工具,怎么能不象征性的支持幾個參數呢?

const config = {
  //從命令行中獲取端口號,如果未設置采用默認
  port: process.argv[2] || 2234,
  hostname: "127.0.0.1"
}
server.listen(config.port, config.hostname, () => {
  console.log(`server is running on http://${config.hostname}:${config.port}`);
});

這里就簡單的舉個栗子啦,大家可以自由發揮!

5. 自動打開瀏覽器

雖然沒太大卵用,但還是要加。我就是要讓你們知道,我加完之后什么樣,你們就是什么樣 :-( duang~

const exec = require("child_process").exec;
server.listen(config.port, config.hostname, () => {
  console.log(`server is running on http://${config.hostname}:${config.port}`);
  exec(`open http://${config.hostname}:${config.port}`);
});

6. process.cwd()

用process.cwd()代替__dirname。

我們最終要做成一個全局并且可以在任意目錄下調用的命令,所以拼接path的代碼修改如下:

//__dirname是當前文件的目錄地址,process.cwd()返回的是腳本執行的路徑
_path = path.join(process.cwd(), _path);

step3 發布

基本上我們的代碼都寫完了,可以考慮發布了!(不發布到npm上何以顯示逼格?)

step3.1 package.json

得到一個配置類似下面所示的json文件:

{
  "name": "here11",
  "version": "0.0.13",
  "private": false,
  "description": "a node static assets server",
  "bin": {
    "here": "./bin/index.js"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/gww666/here.git"
  },
  "scripts": {
    "test": "node bin/index.js"
  },
  "keywords": [
    "node"
  ],
  "author": "gw666",
  "license": "ISC"
}

其中bin和private較為重要,其余的按照自己的項目情況填寫。

bin這個配置代表的是npm i -g xxx之后,我們運行here命令所執行的文件,“here”這個名字可以隨意起。

step3.2 聲明腳本執行類型

在index.js文件的開頭加上:#!/usr/bin/env node

否則linux上運行會報錯。

step3.3 注冊npm賬號

勉強貼一手命令,還不清楚自行百度:

沒有賬號的先添加一個,執行:

npm adduser

然后依次填入

Username: your name
Password: your password
Email: yourmail

npm會給你發一封驗證郵件,記得點一下,不然會發布失敗。

執行登錄命令:

npm login

執行發布命令:

npm publish

關于使用Node.js怎么實現一個靜態資源服務器就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

长岭县| 乌鲁木齐市| SHOW| 铅山县| 罗田县| 海阳市| 太湖县| 安庆市| 蒙城县| 竹北市| 镇坪县| 新野县| 定襄县| 宜昌市| 秦皇岛市| 泽库县| 信宜市| 托里县| 青岛市| 广东省| 丹凤县| 阿勒泰市| 连江县| 邵武市| 星子县| 尉氏县| 郯城县| 石台县| 葫芦岛市| 哈尔滨市| 任丘市| 盐池县| 隆回县| 东乡县| 台安县| 温泉县| 阳朔县| 屏东县| 扶绥县| 班玛县| 黑龙江省|