>

成立贰个服务器能依据不一致的url地址诉求例外

- 编辑:至尊游戏网站 -

成立贰个服务器能依据不一致的url地址诉求例外

研讨前端黑科学技术——通过 png 图的 rgba 值缓存数据

2016/09/12 · JavaScript · 1 评论 · 缓存

最先的作品出处: jrainlau   

谈起前端缓存,大部分人想到的无非是多少个健康的方案,比方cookielocalStoragesessionStorage,或许加上indexedDBwebSQL,以及manifest离线缓存。除此而外,到底还应该有未有其余方法能够开展前端的数额缓存呢?那篇小说将会带你一起来探寻,怎么着一步一步地经过png图的rgba值来缓存数据的黑科学技术之旅。

1.小知识点总括

原理

大家理解,通过为静态能源设置Cache-ControlExpires响应头,能够反逼浏览器对其开展缓存。浏览器在向后台发起呼吁的时候,会先在自己的缓存里面找,假使缓存里面未有,才会三翻五次向服务器恳求那些静态能源。利用那或多或少,大家得以把一些内需被缓存的音讯通过这些静态能源缓存机制来实行仓库储存。

那正是说大家什么样把新闻写入到静态能源中吗?canvas提供了.getImageData()方法和.createImageData()主意,能够分级用于读取设置图片的rgba值。所以大家得以选用那八个API举办信息的读写操作。

接下去看规律图:

图片 1

当静态财富步入缓存,今后的别的对于该图片的倡议都会先找找本地缓存,也正是说消息实际早已以图片的样式被缓存到当地了。

注意,由于rgba值只好是[0, 255]时期的整数,所以本文所争论的法门仅适用于纯数字构成的数目。

* Xshell 使用open张开三个设想机;

静态服务器

小编们运用node搭建三个轻巧的静态服务器:

JavaScript

const fs = require('fs') const http = require('http') const url = require('url') const querystring = require('querystring') const util = require('util') const server = http.createServer((req, res) => { let pathname = url.parse(req.url).pathname let realPath = 'assets' + pathname console.log(realPath) if (realPath !== 'assets/upload') { fs.readFile(realPath, "binary", function(err, file) { if (err) { res.writeHead(500, {'Content-Type': 'text/plain'}) res.end(err) } else { res.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'image/png', 'ETag': "666666", 'Cache-Control': 'public, max-age=31536000', 'Expires': 'Mon, 07 Sep 2026 09:32:27 GMT' }) res.write(file, "binary") res.end() } }) } else { let post = '' req.on('data', (chunk) => { post += chunk }) req.on('end', () => { post = querystring.parse(post) console.log(post.imgData) res.writeHead(200, { 'Access-Control-Allow-Origin': '*' }) let base64Data = post.imgData.replace(/^data:image/w+;base64,/, "") let dataBuffer = new Buffer(base64Data, 'base64') fs.writeFile('assets/out.png', dataBuffer, (err) => { if (err) { res.write(err) res.end() } res.write('OK') res.end() }) }) } }) server.listen(80) console.log('Listening on port: 80')

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
const fs = require('fs')
const http = require('http')
const url = require('url')
const querystring = require('querystring')
const util = require('util')
 
const server = http.createServer((req, res) => {
  let pathname = url.parse(req.url).pathname
  let realPath = 'assets' + pathname
  console.log(realPath)
  if (realPath !== 'assets/upload') {
     fs.readFile(realPath, "binary", function(err, file) {
      if (err) {
        res.writeHead(500, {'Content-Type': 'text/plain'})
        res.end(err)
      } else {
        res.writeHead(200, {
          'Access-Control-Allow-Origin': '*',
          'Content-Type': 'image/png',
          'ETag': "666666",
          'Cache-Control': 'public, max-age=31536000',
          'Expires': 'Mon, 07 Sep 2026 09:32:27 GMT'
        })
        res.write(file, "binary")
        res.end()
      }
   })
  } else {
    let post = ''
    req.on('data', (chunk) => {
      post += chunk
    })
    req.on('end', () => {
      post = querystring.parse(post)
      console.log(post.imgData)
      res.writeHead(200, {
        'Access-Control-Allow-Origin': '*'
      })
      let base64Data = post.imgData.replace(/^data:image/w+;base64,/, "")
      let dataBuffer = new Buffer(base64Data, 'base64')
      fs.writeFile('assets/out.png', dataBuffer, (err) => {
        if (err) {
          res.write(err)
          res.end()
        }
        res.write('OK')
        res.end()
      })
    })
  }
})
 
server.listen(80)
 
console.log('Listening on port: 80')

那些静态能源的效劳非常粗大略,它提供了几个成效:通过顾客端传来的base64生成图片并保存到服务器;设置图片的缓存时间并发送到顾客端。

最首要部分是安装响应头:

JavaScript

res.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'image/png', 'ETag': "666666", 'Cache-Control': 'public, max-age=31536000', 'Expires': 'Mon, 07 Sep 2026 09:32:27 GMT' })

1
2
3
4
5
6
7
res.writeHead(200, {
  'Access-Control-Allow-Origin': '*',
  'Content-Type': 'image/png',
  'ETag': "666666",
  'Cache-Control': 'public, max-age=31536000',
  'Expires': 'Mon, 07 Sep 2026 09:32:27 GMT'
})

大家为那张图纸设置了一年的Content-Type和十年的Expires,理论上丰裕长了。下边大家来拓宽客户端的coding。

* linux 中开创一个空白的日记文件用touch命令;

客户端

XHTML

<!-- client.html --> <canvas id="canvas" width="8", height="1"></canvas>

1
2
3
<!-- client.html -->
 
<canvas id="canvas" width="8", height="1"></canvas>

假定我们须求仓库储存的是33位的数额,所以我们为canvas设置宽度为8,高度为1。到底怎么37人数据对应长度为8,是因为每多少个像素都有三个rgba,对应着redgreenbluealpha4个数值,所以供给除以4。

JavaScript

<!-- client.js --> let keyString = '01234567890123456789012345678901' let canvas = document.querySelector('#canvas') let ctx = canvas.getContext('2d') let imgData = ctx.createImageData(8, 1) for (let i = 0; i < imgData.data.length; i += 4) { imgData.data[i + 0] = parseInt(keyString[i]) + 50 imgData.data[i + 1] = parseInt(keyString[i + 1]) + 100 imgData.data[i + 2] = parseInt(keyString[i + 2]) + 150 imgData.data[i + 3] = parseInt(keyString[i + 3]) + 200 } ctx.putImageData(imgData, 0, 0)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- client.js -->
 
let keyString = '01234567890123456789012345678901'
        
let canvas = document.querySelector('#canvas')
let ctx = canvas.getContext('2d')
 
let imgData = ctx.createImageData(8, 1)
 
for (let i = 0; i < imgData.data.length; i += 4) {
    imgData.data[i + 0] = parseInt(keyString[i]) + 50
    imgData.data[i + 1] = parseInt(keyString[i + 1]) + 100
    imgData.data[i + 2] = parseInt(keyString[i + 2]) + 150
    imgData.data[i + 3] = parseInt(keyString[i + 3]) + 200
}
 
ctx.putImageData(imgData, 0, 0)

第生龙活虎大家假若需求被缓存的字符串为叁拾十二个人的01234567890123456789012345678901,然后大家选用.createImageData(8, 1)转移贰个空白的imgData对象。接下来,大家对那么些空对象开展赋值。为了尝试效果更直观,我们对rgba值都开展了加大。设置完了imgData以后,通过.putImageData()情势把它放入大家的canvas就能够。

大家今后能够打字与印刷一下,看看那个imgData是什么:

JavaScript

// console.log(imgData.data) [50, 101, 152, 203, 54, 105, 156, 207, 58, 109, 150, 201, 52, 103, 154, 205, 56, 107, 158, 209, 50, 101, 152, 203, 54, 105, 156, 207, 58, 109, 150, 201]

1
2
3
// console.log(imgData.data)
 
[50, 101, 152, 203, 54, 105, 156, 207, 58, 109, 150, 201, 52, 103, 154, 205, 56, 107, 158, 209, 50, 101, 152, 203, 54, 105, 156, 207, 58, 109, 150, 201]

接下去,我们要把那一个canvas编写翻译为一张图纸的base64并发送给服务器,相同的时间收纳服务器的响应,对图纸张开缓存:

JavaScript

$.post('', { imgData: canvas.toDataURL() }, (data) => { if (data === 'OK') { let img = new Image() img.crossOrigin = "anonymous" img.src = '' img.onload = (卡塔尔(قطر‎ => { console.log('达成图片要求与缓存')ctx.drawImage(img, 0, 0卡塔尔国 console.log(ctx.getImageData(0, 0, 8, 1卡塔尔国.data卡塔尔(英语:State of Qatar)} } }卡塔尔国

1
2
3
4
5
6
7
8
9
10
11
12
$.post('http://xx.xx.xx.xx:80/upload', { imgData: canvas.toDataURL() }, (data) => {
    if (data === 'OK') {
        let img = new Image()
        img.crossOrigin = "anonymous"
        img.src = 'http://xx.xx.xx.xx:80/out.png'
        img.onload = () => {
            console.log('完成图片请求与缓存')
            ctx.drawImage(img, 0, 0)
            console.log(ctx.getImageData(0, 0, 8, 1).data)
        }
    }
})

代码很简单,通过.toDataURL()措施把base64发送到服务器,服务器管理后生成图片并赶回,其图片能源地址为http://xx.xx.xx.xx:80/out.png。在img.onload后,其实图片就早就到位了本土缓存了,大家在这里个事件此中把图片消息打字与印刷出来,作为和源数据的对峙统生龙活虎。

* http 是nodejs的劳务模块

结果深入分析

展开服务器,运转顾客端,第三回加载的时候经过调节台能够见到响应的图形消息:

图片 2

200 OK,注解是从服务端获取的图形。

关门当前页面,重新载入:

图片 3

200 OK (from cache),表明是从当地缓存读取的图样。

接下去直接看rgba值的相比较:

源数据: [50, 101, 152, 203, 54, 105, 156, 207, 58, 109, 150, 201, 52, 103, 154, 205, 56, 107, 158, 209, 50, 101, 152, 203, 54, 105, 156, 207, 58, 109, 150, 201] 缓存数据:[50, 100, 152, 245, 54, 105, 157, 246, 57, 109, 149, 244, 52, 103, 154, 245, 56, 107, 157, 247, 50, 100, 152, 245, 54, 105, 157, 246, 57, 109, 149, 244]

1
2
3
源数据:  [50, 101, 152, 203, 54, 105, 156, 207, 58, 109, 150, 201, 52, 103, 154, 205, 56, 107, 158, 209, 50, 101, 152, 203, 54, 105, 156, 207, 58, 109, 150, 201]
 
缓存数据:[50, 100, 152, 245, 54, 105, 157, 246, 57, 109, 149, 244, 52, 103, 154, 245, 56, 107, 157, 247, 50, 100, 152, 245, 54, 105, 157, 246, 57, 109, 149, 244]

可以看到,源数据与缓存数据**基本一致**,在`alpha`值的误差偏大,在`rgb`值内**偶有误差**。通过分析,认为产生误差的原因是服务端在进行base64转buffer的过程中,所涉及的运算会导致数据的改变,这一点**有待考证**。

在此之前获得的定论,源数据与缓存数据存在绝对误差的原委,经查明后鲜明为alpha值的搅拌所致。假使大家把alpha值直接定为255,何况只把数量存放在rgb值内部,就能够清除相对误差。下边是修正后的结果:

源数据: [0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 0, 255, 2, 3, 4, 255, 6, 7, 8, 255, 0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 0, 255] 缓存数据:[0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 0, 255, 2, 3, 4, 255, 6, 7, 8, 255, 0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 0, 255]

1
2
3
源数据:  [0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 0, 255, 2, 3, 4, 255, 6, 7, 8, 255, 0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 0, 255]
 
缓存数据:[0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 0, 255, 2, 3, 4, 255, 6, 7, 8, 255, 0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 0, 255]

因为小编懒,只是把alpha值给定为255而尚未把循环赋值的逻辑进行立异,所以第4n位的元数据被向来替换来了255,那么些留着读者自行修改闲暇再改……

总体上看,这几个应用png图的rgba值缓存数据的黑科学和技术,在商议上是立见功效的,但是在实际操作过程中可能还要考虑更多的影响因素,比如设法消除服务端的误差,采取容错机制等。事实上也是行得通的。

值得注意的是,localhost可能默许会直接通过本地并不是服务器央浼能源,所以在本地实验中,可以透过安装header举行cors跨域,何况经过设置IP地址和80端口模拟服务器访谈。

* fs 是文件服务器模块

后记

实属黑科技(science and technology卡塔尔国,其实原理特别轻松,与之相像的还会有通过Etag等方法进行强缓存。钻探的目标只有为了求学,千万不要看成地下之用。假如读者们发掘那篇小说有哪些错漏之处,接待指正,也冀望有意思味的朋友能够一同举行座谈。

多谢你的阅读。作者是Jrain,招待关切本身的特辑,将不按期共享自个儿的求学阅历,开辟心得,搬运墙外的干货。后一次见啦!

1 赞 2 收藏 1 评论

图片 4

* url是url路由模块

2.创建nodejs服务器;

//定义主机IP常量名称

const ip = '192.168.0.102';

//定义端口号

const port = 2000;

//引进的创设立模型块  http、url、fs

const http = require('http');

const url = require('url');

const fs = require('fs');

//创设八个服务

var server = http.createServer(function(req,res){

res.writeHead(200,{'Content-Type':'text/plain'});

res.write('my nodejs');

res.end();

});

//监听二个端口

server.listen(port,ip,function(){

console.log('server start');

});

3.赢得UGL450L部分块的从头到尾的经过 url;

const ip = '192.168.1.118';//主机IP

const port = 2001;//端口号

//引进的创建立模型块  http、url、fs

const http = require('http');

const url = require('url');

const fs = require('fs');

//创造服务的回掉函数

var funSer = function(req,res){

//获取url地址块的原委  如:/path/show

var parth = url.parse(req.url).pathname;

res.write(parth);

res.end();

}

//监听端口的回掉

var fun = function(){

console.log('server start');

}

var server = http.createServer(funSer).listen(port,ip,fun);

4.读取文件的内容 File System;

const ip = '192.168.1.118';//主机IP

const port = 2001;//端口号

//引进的组装模块  http、url、fs

const http = require('http');

const url = require('url');

const fs = require('fs');

//真正打字与印刷文件内容

fs.readFile('./index.html', (err, data) => {

if (err) throw err;

//打印字符串内容

console.log(data.toString());

});

//成立服务的回掉函数

var funSer = function(req,res){

//获取url地址块的剧情  如:/path/show

var parth = url.parse(req.url).pathname;

res.write(parth);

res.end();

}

//监听端口的回掉

var fun = function(){

console.log('server start');

}

var server = http.createServer(funSer).listen(port,ip,fun);

  1. 完全实例(依据差异的url地址央求例外的文本【模板】)

const ip = '192.168.1.118';//主机IP

const port = 2001;//端口号

//引进的组装模块  http、url、fs

const http = require('http');

const url = require('url');

const fs = require('fs');

//实例化二个劳动容器

var server = new http.Server();

//监听一个端口

server.listen(port , ip);

//注册三个事件管理的on方法

server.on('request' , function(req , res){

//获取诉求的url地址

var url = urls.parse(req.url);

//console.log(url.pathname);

//遵照path路线来读取不一致的沙盘模拟经营文件

switch( url.pathname ){

case '' || '/':

//读取文件内容

fs.readFile('./index.html',function( error, content){

if(error卡塔尔{//如若有错误时,展现错误消息

res.writeHead(400,{'Content-Type':'text/plain;charset="utf-8"'});

res.write(error.message);

res.end();

}else{

//正确时浏览器输出模板文件的内容

res.writeHead(200,{'Content-Type':'text/html;charset="utf-8"'});//头信息

res.write(content卡塔尔(قطر‎;//模板文件内容

res.end();

}

});

break;

case '/list':

fs.readFile('./list.html',function( error, content){

if(error){

res.writeHead(400,{'Content-Type':'text/plain;charset="utf-8"'});

res.write(error.message);

res.end();

}else{

res.writeHead(200,{'Content-Type':'text/html;charset="utf-8"'});

res.write(content);

res.end();

}

});

break;

case '/show':

fs.readFile('./show.html',function( error, content){

if(error){

res.writeHead(400,{'Content-Type':'text/plain;charset="utf-8"'});

res.write(error.message);

res.end();

}else{

res.writeHead(200,{'Content-Type':'text/html;charset="utf-8"'});

res.write(content);

res.end();

}

});

break;

default:

fs.readFile('./default.html',function( error, content){

if(error){

res.writeHead(400,{'Content-Type':'text/plain;charset="utf-8"'});

res.write(error.message);

res.end();

}else{

res.writeHead(200,{'Content-Type':'text/html;charset="utf-8"'});

res.write(content);

res.end();

}

});

break;

}

});

本文由门户名站发布,转载请注明来源:成立贰个服务器能依据不一致的url地址诉求例外