当前位置: 首页 > 后端技术 > Node.js

探秘前端黑科技——通过png图片的rgba值缓存数据

时间:2023-04-03 13:11:47 Node.js

..说起前端缓存,大多数人会想到几种常规方案,比如cookie、localStorage、sessionStorage,或者加indexedDB和webSQL,并显示离线缓存。另外,有没有其他办法缓存前端数据呢?本文将带大家通过png图片的rgba值一步步探索如何缓存数据。PS:本文学习的内容已经集成到一个名为SphinxJS的开源JS库中。感兴趣的同学可以移步这篇文章SphinxJS——一个超轻量级的开源库,将字符串编码成png图片。相关文件,欢迎STAR!原理我们知道,通过为静态资源设置Cache-Control和Expires响应头,可以强制浏览器对其进行缓存。当浏览器向后台发起请求时,会先到自己的缓存中查找,如果缓存中没有,就会继续向服务器请求静态资源。利用这一点,我们可以通过这种静态资源缓存机制存储一些需要缓存的信息。那么我们如何将信息写入静态资源呢?Canvas提供了.getImageData()方法和.createImageData()方法,分别用于读取和设置图像的rgba值。所以我们可以使用这两个API来读写信息。接下来看示意图:当静态资源进入缓存后,后续任何对图片的请求都会先去本地缓存中查找,也就是说信息实际上已经以图片的形式缓存在本地了。注意,由于rgba值只能是[0,255之间的整数],所以本文讨论的方法只适用于纯数字组成的数据。静态服务器我们使用node构建一个简单的静态服务器:constfs=require('fs')consthttp=require('http')consturl=require('url')constquerystring=require('querystring')constutil=require('util')constserver=http.createServer((req,res)=>{letpathname=url.parse(req.url).pathnameletrealPath='assets'+pathnameconsole.log(realPath)如果(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,07Sep202609:32:27GMT'})res.write(file,"binary")res.end()}})}else{letpost=''req.on('data',(chunk)=>{post+=chunk})req.on('end',()=>{post=querystring.parse(post)console.log(post.imgData)res.writeHead(200,{'Access-Control-Allow-Origin':'*'})letbase64Data=post.imgData.replace(/^data:image\/\w+;base64,/,"")让dataBuffer=newBuffer(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('侦听端口:80')这个静态资源的功能很简单,它提供了两个功能:通过客户端发送的base64生成图片并保存到服务器;设置图片的缓存时间,发送给客户端关键部分是设置响应头:res。writeHead(200,{'Access-Control-Allow-Origin':'*','Content-Type':'image/png','ETag':"666666",'Cache-Control':'public,max-age=31536000','Expires':'Mon,07Sep202609:32:27GMT'})这张图片我们设置Content-Type为一年,Expires为十年,理论上足够了长的。接下来,让我们进行客户端的编码。Client假设我们需要存储32位的数据,所以我们使用canvasSetwidth为8,height为1。为什么32位数据对应长度为8,是因为每个像素都有一个rgba对应红绿蓝alpha这4个值,所以需要除以4.letkeyString='01234567890123456789012345678901'letcanvas=document.querySelector('#canvas')letctx=canvas.getContext('2d')letimgData=ctx.createImageData(8,1))for(leti=0;i{if(data==='OK'){让img=newImage()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)}}})代码很简单。base64通过.toDataURL()方法发送到服务器。服务端处理后生成图片并返回。图片资源地址为http://xx.xx.xx.xx:80/out.png。img.onload后,图片已经缓存到本地了。我们打印出这个事件中的图像信息作为与源数据的对比。结果分析打开服务器并运行客户端。第一次加载时,通过控制台可以看到响应图片信息:200OK,证明图片是从服务器获取的。关闭当前页面,重新加载:200OK(fromcache),证明图片是从本地缓存中读取的。接下来直接看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]可以是看到source数据和缓存数据基本一致,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]因为我比较懒,所以直接放alpha值给定为255,循环赋值的逻辑没有更新,所以元数据的第4n位直接换成了255,留给读者有时间修改吧。。。总结一下,这个使用png图片rgba值缓存数据的黑科技理论上是可行的,但在实际运行过程中,可能需要考虑更多的影响因素,比如尽量杜绝服务端错误,采用容错机制等。这实际上是可行的。值得注意的是localhost默认可能直接通过本地而不是服务器请求资源,所以在本地实验中可以设置header进行cors跨域,设置IP地址和80端口模拟服务器访问.后记说是黑科技,其实原理很简单。与之类似的还有通过Etag等方式实现的强缓存。研究目的仅供学习,不得用于非法用途。如果读者发现本文有错误或疏漏之处,欢迎指正,也希望有兴趣的朋友一起讨论。谢谢阅读。我是Jrain,欢迎关注我的专栏,我会不定期分享我的学习心得、开发心得、墙外干货。下次见!