Node.js 搭建本地文件服務器

由於想在項目中作一個 mock 數據服務器,目前,做爲項目中的 mock 數據服務器呢,還有幾個問題須要處理。因此先把目前僅僅 「Node.js 搭建本地文件服務器」 的源碼貼出來。javascript

效果

訪問 http://localhost:3000/ 顯示 mock/mockapi/ 下的全部目錄以及文件;
訪問 http://localhost:3000/getUsersInfo.json 顯示 mock/mockapi/getUsersInfo.json 文件數據。css

源碼

// mock/index.js
const http = require('http');
const processRequest = require('./server');
const port = 3000;

const httpServer = http.createServer((req, res) => {
  processRequest(req, res)
})

httpServer.listen(port, () => {
  console.log(`app is running at port: ${port}`);
});

// mock/server.js
const fs = require('fs')
const url = require('url')
const path = require('path')
const mime = require('./mime')

const headers = {
  'Access-Control-Allow-Origin': '*', // 容許跨域
  'Content-Type': 'text/plain'
}

const processRequest = (request, response) => {

  let pathName = url.parse(request.url).pathname
  // 防止中文亂碼
  pathName = decodeURI(pathName)
  // 獲取資源文件的絕對路徑
  let filePath = path.resolve(__dirname + '/mockapi/' + pathName)

  // 文件後綴名
  let ext = path.extname(pathName)
  ext = ext ? ext.slice(1) : 'unknown'
  // 未知類型一概用 "text/plain" 類型
  headers['Content-Type'] = mime[ext] || "'text/plain'"

  // 301重定向
  if (!pathName.endsWith('/') && path.extname(pathName) === '') {
    pathName += '/'
    var redirect = 'http://' + request.headers.host + pathName

    response.writeHead(301, { location: redirect })
    response.end()
  }

  fs.stat(filePath, (err, stats) => {
    // 未找到文件
    if (err) {
      headers['Content-Type'] = 'text/html'
      response.writeHead(404, headers)
      response.end("<h1>404 Not Found</h1>")
    }

    // 文件
    if (!err && stats.isFile()) {
      fs.readFile(filePath, (err, data) => {
        if (err) {
          response.writeHead(500, headers)
          response.end('<h1>500 Server Error</h1>')
        }

        response.writeHead(200, headers);
        response.end(data)
      })
    }

    // 目錄
    if (!err && stats.isDirectory()) {
      var html = '<head><meta charset="utf-8" /></head>'

      fs.readdir(filePath, (err, files) => {
        if (err) {

          html += `<div>讀取路徑失敗!</div>`
          response.writeHead(404, headers)
          response.end(html)

        } else {
          headers['Content-Type'] = 'text/html'
          response.writeHead(200, headers)

          for (var file of files) {
            if (file === 'index.html') {
              response.end(file)
              break
            }

            html += `<div><a href="${file}">${file}</a></div>`
          }
          response.end(html)
        }
      })
    }
  })
}

module.exports = processRequest

// mock/mime.js
module.exports = {
    "css": "text/css",
    "gif": "image/gif",
    "html": "text/html",
    "ico": "image/x-icon",
    "jpeg": "image/jpeg",
    "jpg": "image/jpeg",
    "js": "text/javascript",
    "json": "application/json",
    "pdf": "application/pdf",
    "png": "image/png",
    "svg": "image/svg+xml",
    "swf": "application/x-shockwave-flash",
    "tiff": "image/tiff",
    "txt": "text/plain",
    "wav": "audio/x-wav",
    "wma": "audio/x-ms-wma",
    "wmv": "video/x-ms-wmv",
    "xml": "text/xml"
};

說明

知識點

  • http - HTTP
  • fs - 文件系統
  • path - 路徑
  • url - 網址

建立服務器

http.createServer([options][, requestListener]) 返回一個新得 http.Server 實例;
http.Server類 繼承自 net.Server;
net.Server類 用於建立 TCPIPC serverhtml

server.listen(options[, callback]) 開啓 HTTP 服務器監聽連接。java

mock/server.js 文件 把對 responserequest 的處理封裝成一個匿名函數,傳入 http.createServer() 中。node

// mock/index.js
const http = require('http');
const processRequest = require('./server');
const port = 3000;

const httpServer = http.createServer((req, res) => {
  processRequest(req, res)
})

httpServer.listen(port, () => {
  console.log(`app is running at port: ${port}`);
})

獲取請求資源的路徑

// mock/server.js
const url = require('url')
const path = require('path')

const processRequest = (request, response) => {
  let pathName = url.parse(request.url).pathname
  // 防止中文亂碼
  pathName = decodeURI(pathName)
  // 獲取資源文件的絕對路徑
  let filePath = path.resolve(__dirname + '/mockapi/' + pathName)
}

url.parse()
請求 http://localhost:3000/api/users.json 地址,其中,request.url/api/users.json
請求 http://localhost:3000/api/users.json?a=1 地址,其中,request.url/api/users.json?a=1
因此這裏須要用到,url 模塊 解析URL。json

url 模塊提供了一些實用函數,用於 URL 處理與解析。
url.parse() 方法會解析一個 URL 字符串並返回一個 URL 對象。api

下圖中,網址 http://user:pass@sub.host.com:8080/p/a/t/h?query=string#hashurl.parse()返回的對象的屬性。
圖片描述跨域

這裏,咱們能夠 pathName 即查找文件 相對於 mock/mockapi/ 的路徑。瀏覽器

path.resolve()
有了 pathName,如今就須要獲取資源文件的絕對路徑filePath,方便以後的文件查找以及文件讀取。服務器

path 模塊提供了一些工具函數,用於處理文件與目錄的路徑。
path.resolve([...paths]) 方法會把一個路徑或路徑片斷的序列解析成一個絕對路徑。
例子:

path.resolve('/foo/bar', './baz');
// 返回 '/foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/');
// 返回 '/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// 若是當前工做目錄爲 /home/myself/node,
// 則返回 '/home/myself/node/wwwroot/static_files/gif/image.gif'

重定向

若是是訪問目錄文件,且未以 '/' 結尾,這裏作一下處理,重定向到 訪問路徑尾部添加 '/' 的地址。

// 301重定向
if (!pathName.endsWith('/') && path.extname(pathName) === '') {
  pathName += '/'
  var redirect = 'http://' + request.headers.host + pathName

  response.writeHead(301, { location: redirect })
  response.end()
}

響應頭

後面在返回數據的時候,須要用到 response.writeHead(statusCode[, statusMessage][, headers]) 方法,發送一個響應頭給請求。
狀態碼是一個三位數的 HTTP 狀態嗎,如404。最後一個參數 headers 是響應頭。第二個參數 statusMessage 是可選的狀態描述。

這裏,須要設置響應頭的 Access-Control-Allow-OriginContent-Type

Content-Type,內容類型,通常是指網頁中存在的 Content-Type,用於定義網絡文件的類型和網頁的編碼,決定瀏覽器將以什麼形式,什麼編碼讀取這個文件。

Access-Control-Allow-Origin: <orgin> 指定了該響應的資源是否被容許 與 給定的orgin共享;
* 做爲通配符,容許全部域都具備訪問資源的權限。

const headers = {
  'Access-Control-Allow-Origin': '*', // 容許跨域
  'Content-Type': 'text/plain'
}
const processRequest = (request, response) => {

  let pathName = url.parse(request.url).pathname
  // 防止中文亂碼
  pathName = decodeURI(pathName)

   // 文件後綴名
  let ext = path.extname(pathName)
  ext = ext ? ext.slice(1) : 'unknown'
  // 未知類型一概用 "text/plain" 類型
  headers['Content-Type'] = mime[ext] || "'text/plain'"
}

讀取文件

fs 模塊提供了一些 API,用於以一種相似標準 POSIX 函數的方式與文件系統進行交互。

fs.Stats 對象提供了一個文件的信息。能夠從 fs.stat() 返回。
stats.isDirectory() 若是 fs.Stats 對象表示一個文件系統目錄,則返回 true 。
stats.isFile() 若是 fs.Stats 對象表示一個普通文件,則返回 true 。

fs.readFile(path[, options], callback) 異步地讀取一個文件的所有內容。
fs.readdir(path[, options], callback)異步地讀取一個目錄的內容

fs.stat(filePath, (err, stats) => {
  // 未找到文件
  if (err) {
    headers['Content-Type'] = 'text/html'
    response.writeHead(404, headers)
    response.end("<h1>404 Not Found</h1>")
  }

  // 文件
  if (!err && stats.isFile()) {
    fs.readFile(filePath, (err, data) => {
      if (err) {
        response.writeHead(500, headers)
        response.end('<h1>500 Server Error</h1>')
      }

      response.writeHead(200, headers);
      response.end(data)
    })
  }

  // 目錄
  if (!err && stats.isDirectory()) {
    var html = '<head><meta charset="utf-8" /></head>'

    fs.readdir(filePath, (err, files) => {
      if (err) {

        html += `<div>讀取路徑失敗!</div>`
        response.writeHead(404, headers)
        response.end(html)

      } else {
        headers['Content-Type'] = 'text/html'
        response.writeHead(200, headers)

        for (var file of files) {
          if (file === 'index.html') {
            response.end(file)
            break
          }

          html += `<div><a href="${file}">${file}</a></div>`
        }
        response.end(html)
      }
    })
  }
})

返回數據

response.end([data][, encoding][, callback])
該方法會通知服務器,全部響應頭和響應主題都已被髮送,即服務器將其視爲已完成。每次響應都必須調用 response.end() 方法。

若是指定了 data,則至關於調用 response.write(data, encoding) 以後再調用 response.end(callback)

若是指定了 callback,則當響應流結束時被調用。

response.writeHead(404, headers)
response.end('<h1>500 Server Error</h1>')

Node.js 中文網

相關文章
相關標籤/搜索