我们来写一个最简单的静态服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
const http = require('http') const fs = require('fs') const path = require('path')
http.createServer((req, res) => { fs.readFile(path.join(__dirname, req.url), (err, data) => { if (err) { res.writeHead(404, 'not found') res.end('Oh, Not Found') } else { res.writeHead(200, 'OK') res.end(data) } }) }).listen(8000) console.log('Visit http://localhost:8000' )
|
想要实现的功能:当用户输入地址时,读取当前文件夹的 index.html 文件。
使用 fs.readFile 方法读取文件
使用绝对路径 + req.url 构造最完整路径
如果不存在,写一个响应报文 404,返回一个错误提示信息字符串
如果存在,写一个响应报文 200,返回相应数据
我们使用 node index.js
进行启动之后,可以在浏览器中访问到对应的页面内容。
Expires 和 pragma
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
const http = require('http') const fs = require('fs') const path = require('path')
http.createServer((req, res) => { let filePath = path.join(__dirname, req.url) fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, 'not found') res.end('Oh, Not Found') } else {
res.setHeader('Expires', 'Wed, 23 Jan 2019 07:40:51 GMT')
let date = new Date(Date.now() + 1000*5).toGMTString() res.setHeader('Expires', date)
res.writeHead(200, 'OK') res.end(data) } }) }).listen(8080) console.log('Visit http://localhost:8080' )
|
我们可以看到响应的图片文件:
1 2 3 4
| Connection: keep-alive Date: Wed, 20 Feb 2019 05:15:26 GMT Expires: Wed, 20 Feb 2019 05:15:31 GMT Transfer-Encoding: chunked
|
设置 Expires 之后我们可以在浏览器检查的 Network Size 中看到资源的来源是 memory cache 或者 disk cache
如果我们不走 Expires 而是 Pragma:
告诉浏览器不要走本地的缓存,而是走服务器。
Cache Control 字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
const http = require('http') const fs = require('fs') const path = require('path')
http.createServer((req, res) => { let filePath = path.join(__dirname, req.url) fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, 'not found') res.end('Oh, Not Found') } else {
res.setHeader('Cache-Control', 'no-store')
res.writeHead(200, 'OK') res.end(data) } }) }).listen(8080) console.log('Visit http://localhost:8080' )
|
Last-Modified 字段对缓存的控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| const http = require('http') const fs = require('fs') const path = require('path')
http.createServer((req, res) => { let filePath = path.join(__dirname, req.url) fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, 'not found') res.end('Oh, Not Found') } else {
let mtime = Date.parse(fs.statSync(filePath).mtime) res.setHeader('Cache-Control', 'max-age=10')
if(!req.headers['if-modified-since']) { res.setHeader('Last-Modified', new Date(mtime).toGMTString()) res.writeHead(200, 'OK') res.end(data) }else { let oldMtime = Date.parse(req.headers['if-modified-since']) if(mtime > oldMtime) { res.setHeader('Last-Modified', new Date(mtime).toGMTString()) res.writeHead(200, 'OK') res.end(data) }else { res.writeHead(304) res.end() } }
} }) }).listen(8080) console.log('Visit http://localhost:8080' )
|
当我们的请求到达之后,我们使用 stat 获取文件的很多基本信息。
其中有一个 mtime 也就是文件的修改时间,我们来获取他,接着解析出这个时间,将它转换为 GMT 格式的事件对象。
Etag 字段Etag 字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| const http = require('http') const fs = require('fs') const path = require('path') const crypto = require('crypto')
http.createServer((req, res) => { let filePath = path.join(__dirname, req.url) fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, 'not found') res.end('Oh, Not Found') } else { // example1 // let md5 = crypto.createHash('md5') // res.setHeader('Etag', md5.update(data).digest('base64')) // res.writeHead(200, 'OK') // res.end(data)
// example2 console.log(req.headers['if-none-match']) let oldEtag = req.headers['if-none-match'] if(!oldEtag) { let md5 = crypto.createHash('md5') res.setHeader('Etag', md5.update(data).digest('base64')) res.writeHead(200, 'OK') res.end(data) } else { let newEtag = crypto.createHash('md5').update(data).digest('base64') if(oldEtag !== newEtag) { res.setHeader('Etag', newEtag) res.writeHead(200, 'OK') res.end(data) }else { res.writeHead(304) res.end() } }
//example2 // console.log(req.headers) // let mtime = Date.parse(fs.statSync(filePath).mtime) // if(!req.headers['if-modified-since']) { // res.setHeader('Last-Modified', new Date(mtime).toGMTString()) // res.writeHead(200, 'OK') // res.end(data) // }else { // let oldMtime = Date.parse(req.headers['if-modified-since']) // if(mtime > oldMtime) { // res.setHeader('Last-Modified', new Date(mtime).toGMTString()) // res.writeHead(200, 'OK') // res.end(data) // }else { // res.writeHead(304) // res.end() // } // }
//example3 // let mtime = Date.parse(fs.statSync(filePath).mtime) // //10秒内,浏览器直接从自己本地拿,10秒后找服务器要。如果没修改,告诉浏览器没修改就行,如果修改了,给浏览器最新的 // res.setHeader('Cache-Control', 'max-age=10')
// if(!req.headers['if-modified-since']) { // res.setHeader('Last-Modified', new Date(mtime).toGMTString()) // res.writeHead(200, 'OK') // res.end(data) // }else { // let oldMtime = Date.parse(req.headers['if-modified-since']) // if(mtime > oldMtime) { // res.setHeader('Last-Modified', new Date(mtime).toGMTString()) // res.writeHead(200, 'OK') // res.end(data) // }else { // res.writeHead(304) // res.end() // } // }
} }) }).listen(8080) console.log('Visit http://localhost:8080' )
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| const http = require('http') const fs = require('fs') const path = require('path') const crypto = require('crypto')
http.createServer((req, res) => { let filePath = path.join(__dirname, req.url) fs.readFile(filePath, (err, data) => { if (err) { res.writeHead(404, 'not found') res.end('Oh, Not Found') } else {
console.log(req.headers['if-none-match']) let oldEtag = req.headers['if-none-match'] if(!oldEtag) { let md5 = crypto.createHash('md5') res.setHeader('Etag', md5.update(data).digest('base64')) res.writeHead(200, 'OK') res.end(data) } else { let newEtag = crypto.createHash('md5').update(data).digest('base64') if(oldEtag !== newEtag) { res.setHeader('Etag', newEtag) res.writeHead(200, 'OK') res.end(data) }else { res.writeHead(304) res.end() } }
} }) }).listen(8080) console.log('Visit http://localhost:8080' )
|