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

因为想在项目中做一个 mock 数据服务器,目前,作为项目中的 mock 数据服务器呢,还有几个问题需要处理。所以先把目前仅仅 “Node.js 搭建本地文件服务器” 的源码贴出来。

效果

访问 http://localhost:3000/ 显示 mock/mockapi/ 下的所有目录以及文件;
访问 http://localhost:3000/getUsersInfo.json 显示 mock/mockapi/getUsersInfo.json 文件数据。

源码

// 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("

404 Not Found

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

500 Server Error

') } response.writeHead(200, headers); response.end(data) }) } // 目录 if (!err && stats.isDirectory()) { var html = '' fs.readdir(filePath, (err, files) => { if (err) { html += `
读取路径失败!
` 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 += `` } 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 server

server.listen(options[, callback]) 开启 HTTP 服务器监听链接。

mock/server.js 文件 把对 responserequest 的处理封装成一个匿名函数,传入 http.createServer() 中。

// 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。

url 模块提供了一些实用函数,用于 URL 处理与解析。
url.parse() 方法会解析一个 URL 字符串并返回一个 URL 对象。

下图中,网址 http://user:[email protected]:8080/p/a/t/h?query=string#hashurl.parse()返回的对象的属性。
Node.js 搭建本地文件服务器_第1张图片

这里,我们可以 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共享;
* 作为通配符,允许所有域都具有访问资源的权限。

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("

404 Not Found

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

500 Server Error

') } response.writeHead(200, headers); response.end(data) }) } // 目录 if (!err && stats.isDirectory()) { var html = '' fs.readdir(filePath, (err, files) => { if (err) { html += `
读取路径失败!
` 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 += `` } 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('

500 Server Error

')

Node.js 中文网

你可能感兴趣的:(node.js)