Web Server之处理静态文件
来源:互联网 发布:js基础知识定义变量 编辑:程序博客网 时间:2024/05/13 10:40
Web Server之处理静态文件
标签(空格分隔): webserver
细心的同学会发现,在express server的代码中,包含这么一句app.use('/static', express.static('public'));
,什么用呢?这个是用来处理静态文件,所有访问/static
及其子目录下的url均会检查静态文件,如果静态文件存在,则直接返回静态资源。
是不是很简单,但是我们还得深挖一下,看这货到底是怎么实现的。
handle static file
我们还是通过原生态的httpServer,新建一个静态文件的处理流程。
var http = require("http");var url = require("url");var fs = require("fs");var mimeType = require("mime-types");http.createServer(function(req, res){ var obj = url.parse(req.url); var pathname = obj.pathname; if(pathname.indexOf("/static")===0){ var fileName = __dirname + pathname; fs.stat(fileName, function(err, is){ if(err){ console.error(err); res.writeHead(500, "Error occur."); res.end(); } else{ if(is.isFile()){ fs.readFile(fileName, function(err, buf){ if(err){ console.error(err); res.writeHead(500, "Read file error."); res.end(); } else{ var tempObject = { "Content-Type" : mimeType.lookup(pathname), "Last-Modified": is.mtime.toLocaleString(), "Content-Length": is.size }; res.writeHead(200, tempObject); res.write(buf); res.end(); } }); } else{ res.writeHead(404, "Not Found"); res.end(); } } }); } else{ res.writeHead(200); res.write("Hello!"); res.end(); }}).listen(3001);
代码解析
- 获取模块
http
、url
、fs
、mime-types
- 新建一个httpServer实例,并绑定端口3001.
- 定义httpServer的处理逻辑,对于/static开头的请求,均按照静态文件进行处理,如果文件存在,则将文件内容返回值Response对象,否则则返回404;
API介绍
看着代码,两眼一抹黑!这是什么鬼,怎么会如此之多,而且req、res这是啥,怎么中知道哪些方法能用,哪些属性能访问。这个我只能告诉你,平时多看书,需要时自觉上网查资料。此处我们不妨列举一下用到的对象。
http.Server
通过http.createServer
创建一个Server实例
方法
server.close([callback]) server不在接收新的端口
server.listen(param, [callback]) 监听端口或者一个Unix Socker path。
其他,具体参考https://nodejs.org/api/http.html#http_class_http_server
事件
checkContinue
clientError
close
connect
connection
request
upgrade
http.ServerResponse
http请求一旦建立,会产生一个request实例(IncomingMessage)和response实例(http.ServerResponse),下面介绍一下常用的方法。
方法
response.end(data, [encoding], [callback]) 通过该方法告诉服务器所有的header和body均已经发送。因此每个http请求均需要调用该方法来结束处理,否则服务器将持续等待。
response.finished 在调用end方法之前,该值为false,调用之后该值为false,可以通过该方法判断response是否调用end()方法。
response.getHeader(name) 获取指定header的值
response.removeHeader(name) 删除指定header
response.setHeader(name, value)
response.writeHead(code[, codeMessage][, header]) 发送header,包括状态码,调用此方法之后headersSent将置为true。
response.headerSent 只读,表示headers是否被发送。
response.write(chuck, [encoding], [callback]) 异步发送数据。
事件
close
finish
http.IncomingMessage
request的类。
方法
request.destroy([error])
request.headers 一个object对象,内部的key使用小写格式,如content-type、last-modified等
request.httpVersion http版本,如1.1/1.0
request.method http使用的方法,如GET、POST、DELETE等
request.rawHeaders 原始header,request.headers是经过滤重处理的,而request.rawHeaders未经过任何处理。
request.url, url属性,可以通过url模块处理该字段。
fs.Stats
通过fs.stat(filePath, callback)可以在callback中得到一个Stats对象,Stats对象包括的属性和方法如下:
属性&方法
stats.isFile() 是否文件
stats.iDirectory() 是否文件夹
stats.isBlockDevice() 是否块设备
stats.isCharacterDevice() 是否字符设备
stats.isSymbolicLink() 是否链接
stats.isFIFO() 是否管道文件
stats.isSocket() 是否Socket文件
属性(节选)
size 文件大小,单位字节
blksize: 块大小,单位字节
atime: access time,Date对象
mtime: modified time, Date对象,上次更改时间,
ctime: change time, Date对象,上次更改时间,重命名,更改权限等均能修改该属性。
birthtime: Birth Time,创建时间。
url.parse方法
一个url通过url.parse方法会返回一个object对象,对象格式如下:
{ href: "full url", protocol : "", // 协议 slashes: true | false, // host: "", // hostname + port, 如 "host.com:8080" auth: "", // 授权信息,如URL(http://)"user:pass", hostname: "", // host信息,不包括端口, port: number, // 端口信息,如http的80, ftp的21 pathname: "", // 文件地址,如abc/d/e.txt search: "", // query部分,如?query=string path: "" // pathname + search集合}
更深一步
对于静态资源而言,一但生成,修改的可能性一般都不大,在实际操作过程,我们可以利用缓存以减少带宽的压力。
一般地,浏览器会根据header中的字段,判断是否利用缓存。因此,我们可以将代码略做修改:
>
1. 获取request中的If-Modified-Since字段,如果非空,比当前文件的last modified time进行比较,如果If-Modified-Since小于last modified time,进入第3步,否则直接返回304.
2. 获取request中的If-None-Match字段,如果字段非空,且If-None-Match等于文件的MD5值,直接返回304,否则进入第3步。
3. 读取文件,设置Response的Last-Modified、Expires、Cache-Control、ETag字段。
// 处理修改时间var lastModifiedTime = is.mtime;var ifModifiedSince = req.headers["if-modified-since"];if(ifModifiedSince){ ifModifiedSince = new Date().strtotime(ifModifiedSince); if(!(ifModifiedSince instanceof Date)){ ifModifiedSince = null; }}if(ifModifiedSince && ifModifiedSince.getTime() >= lastModifiedTime.getTime()){ res.writeHead(304); res.end(); return ;}// 处理etagvar et = eTag(is);var ifNoneMatch = req.headers["if-none-match"];if(ifNoneMatch && et === ifNoneMatch){ res.writeHead(304); res.end(); return ;}// 如果modified和etag校验均失败,返回静态文件内容。var tempObject = { "Expires": new Date().strtotime("30 days").format("ddd, dd mmm yyyy HH:MM:ss Z"), "Content-Type" : mimeType.lookup(pathname), "Last-Modified": lastModifiedTime.format('ddd, dd mmm yyyy HH:MM:ss Z'), "eTag": et, "Content-Length": is.size, "Cache-Control": "public,max-age=2592000000"};res.writeHead(200, tempObject);res.write(buf);res.end();
代码分析
- 获取request header中的If-Modified-Since值,并与当前文件的Last Mofidified Time比较;如果If-Modified-Since大于等于Last Mofidified Time,则表示服务器文件并未更新,直接返回304;
- 通过etag包的方法当前文件的etag值,并获取request header中If-None-Match值,两者对比;
- 上述比配均失败时,获取文件内容,并在header中写入Expires、Last-Modified、Etag。
再深一步
我们可以通过浏览器验证一下,执行如下代码:
var loadJS = function(src){ var script = document.createElement("script"); script.type = "text/javascript"; script.src = src; document.body.appendChild(script);}
然后执行loadJS("http://localhost:3001/static/js/index.js");
10次。监控网络状况,可以发现如下情况:
浏览器只发送了一次请求!!!
此时我们再使用浏览器打开http://localhost:3001/static/js/index.js
,发现网络请求如下:
没有意外,直接返回304。使用调试工具,查看网络请求,如下图:
我们可以看出,在request请求中包含了两个header
* If-Modified-Since
* If-None-Match
那么问题来了,浏览器为什么会只发送一个请求:
当浏览器首次获取资源时,资源返回内容并附带Expires,Cache-Control,Last-Modified,ETag;当浏览器再次请求时,浏览器根据Expires、Cache-Control,判断是否从cache中返回,此例中,直接从cache中返回,http状态码为200;此后无论发送多少次请求,只要Cache没有过期,均从cache中获取。
附:浏览器缓存操作:
参考文献
- Node.js API: https://nodejs.org/dist/latest-v4.x/docs/api/
- 浏览器缓存: http://www.alloyteam.com/2012/03/web-cache-2-browser-cache/
- Web Server之处理静态文件
- 新浪SAE python web静态文件处理
- Golang Web 框架 Beego 静态文件处理
- Django web server: html引用静态文件(css, js)
- web服务器与应用服务器静态文件处理
- nginx静态文件处理
- Nginx静态文件处理
- SpringMVC 处理静态文件
- Web静态资源处理方案
- Golang 开发web应用时的静态文件处理方法(v0.01)
- Django 静态文件处理 三部曲
- Django静态文件处理总结
- spring mvc处理静态文件
- Django静态文件处理总结
- Django静态文件处理总结
- Spring MVC静态文件处理
- 1.3.2 处理静态文件
- 静态文件的缓存处理
- 图形绘制布局开发_如何绘制几何图形(LuaJava)
- UISearchController 隐藏tabbar的一个方法
- MySQL case when then else end 用法
- 手机号码有效性判断
- 代码注释2016-8-5
- Web Server之处理静态文件
- 连接mysql
- 多线程之NSOperation
- 数组中出现次数超过一半的数字
- File 获取文件名 路径 测试结果
- comment的用法
- Win 编译使用libevent
- JAVA CAS原理深度分析
- Android 图片的三级缓存 及 图片压缩