服务端托管静态文件 理解

什么是 解静态文件服务


在web 的领域,一切请求全都是为了资源,资源也可以任意的形式,文本文件,图片文件, 字符片段等等。只要明白了这一点,理解静态文件服务就相对轻松了。

在服务器上,一切都是资源, 我们发出的任何请求,都是请求资源。当我们在浏览器中输入www.baidu.com的时候,我们是向www.baidu.com服务器请求index.html资源,发送的请求是get请求。页面中可能会有css, img, js等文件,当html 文件在进行解析的时候,它碰到css, img, js时,就会向服务器发送请求,请求这些资源,这些请求也是get 请求。我们平时写js 代码,发送ajax 请求, 它请求的也是资源,只不过通常是json 字符串或xml。


express static 实现静态文件服务器


现在用express 搭建一个服务器,来实战理解一下express.static 托管静态资源。

  1. 初始化express项目:
    1)新建一个目录,就叫 express_static 吧,
    2)npm init -y 快速创建package.json文件,
    3)npm install express --save 安装express 依赖。

  2. 新建文件 server.js,来写服务器代码。我们先按照Express的官网的要求使用express.static中间件。
    1)引入express模块:const express = require(‘express’);
    2)然后创建服务器:const app = express();
    3)然后通过express.static给服务器提供静态文件访问: app.use(express.static(‘目录名’))

  3. 再新建一个public文件夹来存放上面说的要托管的文件:
    在里面建index.html 文件,和css 文件。

整个目录结构如下:

index.html 中随便写点什么,我们就写一个h1好了,还要引入css, 文件如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <link rel="stylesheet" href="./style.css">
</head>
<body>
    <h1>express.static 托管静态资源</h1>
</body>
</html>

css也简单点,给h1 一个color :

h1 {  color: red; }

server.js 服务端代码:

var express = require('express');
var path = require('path');

var app = express();

// 利用express.static中间件来托管静态资源。
app.use(express.static(path.join(__dirname, 'public')));

app.listen(5000, () => {
    console.log('start: 5000')
})

服务器启动后,打开浏览器,输入localhost:5000, 它确定显示了index.html的内容,并且也应用到了css 样式,确实是访问到了静态文件html, css.

刚才在上面说了,当我们在浏览器地址栏中,输入网址时,它是在请求资源index.html,html在解析的过程中碰到css, 它也是在请求资源,css文件,我们来看一下是不是这样,这样用到浏览器控制台中的NetWork 板块。打开控制台,找到Network版块,可以看到,它确实是发送了两个请求,一个是localhost, 一个是style.css,和我们预想的一样。

点击localhost 的看一下具体的内容:
 发送的url : http://localhost:5000/, 请求方式是Get 请求。点到图中的Preview 或Response看一下, 返回的是index.html 文件。

image

再来看一下style.css:
它请求的地址是 http://localhost:5000/style.css,我们在index.html中写的是 href='./style.css' 相对路径,但在真实的请求中,它是向服务器根目录请求的资源。当然方法还是get 请求。Response是我们写的css 文件。

image

静态文件的访问请求,服务器确实是帮我们处理好了,这就是 express.static 的功劳。
验证:把 express.static 的那段代码注释掉,重新启动服务, 任何资源都访问不到。

看一下控制台的Network, 404 not found, 也就是说,服务器没有处理这个请求的代码。

express.get 访问静态资源

如果不用 express.static ,还可以用什么方法来访问静态资源?

我们知道,它这里其实是发送了一个get 请求,请求的地址是http://localhost: 5000, 根据这个请求内容,我们可以自己写一个处理请求,其实它就是路由,get 请求用到的是app.get, 由于这里的请求是服务器根地址,所以路径是'/', 响应就是index.html文件,这里用到是sendfile 来发送文件, 代码如下:

app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public/index.html'))
})

这样下来,可以看到页面显示文字了,但是字体样式没有生效,看下控制台,style.css 状态是404 not found

这时再看控制台, localhost 请求成功了,style.css 是红色的,状态是404 not found, 也就是没有找到资源

image

这时我们已经知道了, 浏览器向服务器请求了css 资源,但是在服务端并没有处理这个请求,所以就报404的错误,这时和处理html 一样,我们要写一个路由,来处理css请求,这里还是get请求,所以app.get, 因为请求的是所有的css文件,请求的处理路径要写成’*.css’, 文件的发送还是用sendFile.

app.get('*.css', (req, res) => {
    res.sendFile(path.join(__dirname, 'public/style.css'))
})

这时我们没有使用express.static 静态服务中间件, 而是自已写的路由,你会发现作用是一样的。我们的页面中还没有js, image 等资源,如果有的话,我们还需要写处理js文件和图片文件的路由,分别对应js 和image 文件的请求。

到这里就明白了,html,js, css, image 都是存放在服务端的资源,当在浏览器中输入网址的后,我们就是向服务器请求这些资源,相对应的服务端就要有对应的路由来处理这些请求,否则就是404 not found 错误。对于这些静态文件的路由处理,代码逻辑都是一致的,就是返回请求的文件。

再往大处想一想,我们每写一个网站的服务端,都要处理这些静态文件的请求, 因为几乎所有的html文件是都包括js, css, image 文件, 是不是太麻烦了,我们可以复制粘贴,但也是不太好,最好的办法,当然是把这些一致的处理逻辑抽离出来,形成一个单独的组件,直接使用组件就好了,这就是express.static 中间件的由来,它就是处理js, css, image等静态文件的请求,它把我们对静态文件的路由处理封装起来。 它的参数是一个文件夹,就是当有请求过来的时候,直接到该文件来下寻找资源,如果有,就直接返回文件,程序不再向下执行,如果找不到,程序再向下执行。那我们直接把静态文件放到该文件夹,就不用写这些静态文件的路由了。

express.static 注意点

1)中间件的放置位置
这里还要注意一点,express.static 中间件的使用,要放在路由之前。路由的处理就是有一个匹配就返回,我们的 express.static 本质上也是一个路由。如果在express.static之气有的其它请求处理这些静态文件的请求,就不会再轮不到 express.static 来进行执行了。

2)中间件托管文件的路径
在express的官网,express.static 还有一种使用情况, 就是两个参数,第一个参数是一个路径,第二个参数才是express.static 的使用。

app.use('/static', express.static('public'));

第一个参数路径,表示的是我们的静态文件的引入路径的前面有一个路径/static ,比如,我们的index.html中引入css 改成如下方式

<link rel="stylesheet" href=" /static/style.css">  

css 的资源请求的路径前面肯定会再一个static目录,http://localhost:5000/static/style.css,因为前端资源请求前面有static路径, 所以我们静态文件的路由处理,前面也要加static路径进行匹配。

var express = require('express');
var path = require('path');

var app = express();

// 因为前端资源请求前面有static路径, 所以我们静态文件的路由处理,前面也要加static路径进行匹配。
app.use('/static', express.static(path.join(__dirname, 'public')));

// 为了处理html 文件的请求。
app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, 'public/index.html'))
})
app.listen(5000, () => {
    console.log('start: 5000')
})


node 实现静态文件服务器


先来看看nodejs怎么读取本地文件。以读取上面的 /public/index.html 为例

根目录新键 serve2.js 文件:
1)http.createServer 创建服务
2)fs.readFile 读取文件

const http = require('http')
const fs = require('fs')
const url = require('url')
const path = require('path')

const PORT = 3001

function serverStatic (req, res) {
  let filePath
  if (req.url === "/") {
    filePath = "/public/index.html"
  } else {
    filePath = "./public/" + url.parse(req.url).pathname
  }

  let filePathFull = path.join(__dirname, filePath)
 // console.log("filePathFull: ", filePathFull)

  fs.readFile(filePathFull, function (err, data) {
    if (err) {
      res.end("<h1>500</h1>服务器内部错误!")
      // console.log("err: ", err)
    } else {
      res.writeHead(200, { 'content-type': 'text/html'})
      res.end(data.toString())
    }
  })
}

const server = http.createServer(serverStatic);
server.listen(PORT, function () {
  console.log("Server runing at port: " + PORT + ".")
})

根目录下以 node 命令启动服务:

node serve2.js

再浏览器打开 http://localhost:3001/,可以看到页面,但是样式没有生效。虽然 style.css 我们也访问了,但是我们在上面设置死了 content-type:text/html,而 style.css 的 contentType 应该为 text/css,所以应该根据我们访问文件的后缀来动态设置。

我们来优化下 serve2.js :

const http = require('http')
const fs = require('fs')
const url = require('url')
const path = require('path')

//访问端口号
const PORT = 3001

//添加MIME类型
var MIME_TYPE = {
  "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"
}

function serverStatic (req, res) {
  let filePath
  if (req.url === "/") {
    filePath = "/public/index.html"
  } else {
    filePath = "./public/" + url.parse(req.url).pathname
  }

  let filePathFull = path.join(__dirname, filePath)
  console.log("filePathFull: ", filePathFull)

  let ext = path.extname(filePathFull);
  ext = ext ? ext.slice(1) : 'unknown';

  let contentType = MIME_TYPE[ext] || "text/plain"

  fs.readFile(filePathFull, function (err, data) {
    if (err) {
      res.end("<h1>500</h1>服务器内部错误!")
      console.log("err: ", err)
    } else {
      res.writeHead(200, { 'content-type': contentType })
      res.end(data.toString())
    }
  })
}

const server = http.createServer(serverStatic);
server.listen(PORT, function () {
  console.log("Server runing at port: " + PORT + ".")
})

优化好后,重启服务,访问页面,就能看到 style.css 文件生效了。


应用:本地访问 vue 打包后的文件


vue 项目 build 之后的包(这里设为dist文件夹),如果想在本地先预览,可以怎么设置?

即访问本地index.html文件,所以我们需要启动一个本地服务,还需要根据访问不同的文件设置不同 contentType,还需要在刷新时重定向回index.html上:

const express = require("express")
const path = require("path")

const app = express()

const port = 3001

let history = require('connect-history-api-fallback')

//重定向到index.html
history({
  rewrites: [{
    from: /^\/libs\/.*$/,
    to: '/index.html'
  }]
});

app.use(history());

app.use(express.static(path.join(__dirname, 'dist')));

app.listen(port, () => {
  console.log('Listening at http://localhost:' + port)
})



相关:
Express static 托管静态文件 理解
原生koa2实现静态资源服务器
koa-static中间件使用
HTML5 History模式 后端配置

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容