将js/css脚本放到png图片中的实践。

起因
高级浏览器支持data协议,如:
<img   src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC" />
<script   src = "data:text/javascript;base64,YWxlcnQoJ2h0dHA6Ly93ZWliby5jb20venN3YW5nJyk7" ></script>
不光是<img>标签,<script>、<style>、<iframe>也都支持data协议;
data协议可以表示图片、文本、音视频等各种二进制数据
另html5的canvas除了绘制图像,还能读写每一个像素值
一种把脚本存到图像中的思路就有了。

经过
最初的尝试就是把每个字符挨个放到像素的r、g、b、a里
需要处理的问题:
1、计算图片的尺寸;
     本来想用高度为1,宽度为字符串长度的尺寸,但考虑到压缩比和美观,所以决定生成正方形。
         var  pixel = Math.ceil((text.length +  3 ) /  4 );
         var  size = Math.ceil(Math.sqrt(pixel));
2、每个字符unicode编码,可能会超出255;
     那就得将字符串转成ascii码
function  encodeUTF8(str) {
     return  String(str).replace(
         /[\u0080-\u07ff]/g ,
         function (c) {
             var  cc = c.charCodeAt( );
             return  String.fromCharCode( 0xc0  | cc >>  6 ,  0x80  | cc &  0x3f );
        }
    ).replace(
         /[\u0800-\uffff]/g ,
         function (c) {
             var  cc = c.charCodeAt( );
             return  String.fromCharCode( 0xe0  | cc >>  12 ,  0x80  | cc >>  6  &  0x3f ,  0x80  | cc &  0x3f );
        }
    );
}
3、还得考虑异步的问题
     var  img = document.createElement( 'img' );
    img.onload =  function (){
        //... ;
    }

第一轮demo做完,结果测试不符合预期。跟踪发现:还原的数据和编码的数据相差较大。
开始在怀疑是不是img标签绘制到canvas变成了有损压缩,如果真是这样,那整个方案就是不可行;
坚持“不抛弃,不放弃”+定位问题最简化的原则,逐步缩小范围,最终锁定是由于alpha值影响还原。
解决方案就是:一个像素存放三个字符并将alpha固定为255。

第二轮demo测试符合预期。

总结
build流程:
1、字符串转换成ascii码;
2、创建足够存储空间的canvas;
3、将字符填入到像素中(忽略alpha值);
4、获取data url;
     canvas.toDataURL( "image/png" );
5、存为png图片。
代码示例
  1. <textarea id="base64"></textarea>  
  2. <script>  
  3. function encodeUTF8(str) {  
  4.     return String(str).replace(  
  5.         /[\u0080-\u07ff]/g,  
  6.         function(c) {  
  7.             var ccc = c.charCodeAt(0);  
  8.             return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);  
  9.         }  
  10.     ).replace(  
  11.         /[\u0800-\uffff]/g,  
  12.         function(c) {  
  13.             var ccc = c.charCodeAt(0);  
  14.             return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f);  
  15.         }  
  16.     );  
  17. }  
  18.   
  19. function request(url, loaded) {  
  20.     var xmlhttp = new XMLHttpRequest();  
  21.     xmlhttp.onreadystatechange = function() {  
  22.         if (xmlhttp.readyState == 4)  
  23.             if (xmlhttp.status == 200)  
  24.                 loaded(xmlhttp);  
  25.     }  
  26.     xmlhttp.open("GET", url, true);  
  27.     xmlhttp.send();  
  28. }  
  29.   
  30. void function(){  
  31.     var source = 'tangram-1.5.0.js';  
  32.     request(source, function(xmlhttp){  
  33.         var text = encodeUTF8(xmlhttp.responseText);  
  34.         var pixel = Math.ceil((text.length + 2) / 3); // 1一个像素存3个字节,  
  35.         var size = Math.ceil(Math.sqrt(pixel));  
  36.         //console.log([text.length, pixel, size, size * size * 3]);  
  37.         var canvas = document.createElement('canvas');  
  38.         canvascanvas.width = canvas.height = size;  
  39.         var context = canvas.getContext("2d"),  
  40.             imageData = context.getImageData(0, 0, canvas.width, canvas.height),  
  41.             pixels = imageData.data;  
  42.         for(var i = j = l = pixels.length; i < l; i++){  
  43.             if (i % 4 == 3) { // alpha会影响png还原  
  44.                 pixels[i] = 255;  
  45.                 continue;  
  46.             }  
  47.             var code = text.charCodeAt(j++);  
  48.             if (isNaN(code)) break;  
  49.             pixels[i] = code;  
  50.         }  
  51.         context.putImageData(imageData, 0, 0);  
  52.         document.getElementById('base64').value = canvas.toDataURL("image/png");  
  53.     });  
  54. }();  
  55. </script>  
编译结果


调用流程:
1、加载png;
2、将png原尺寸绘制到canvas中;
3、读取像素中的字符串;
4、生成相应协议的data url使用。
代码示例
  1. <script>  
  2. void function(){  
  3.     var source = 'tangram-1.5.0.png';  
  4.     var img = document.createElement('img');  
  5.     img.onload = function(){  
  6.         var canvas = document.createElement('canvas');  
  7.         canvas.width = img.width;  
  8.         canvas.height = img.height;  
  9.   
  10.         var context = canvas.getContext("2d");  
  11.         context.drawImage(img, 0, 0);  
  12.         var imageData = context.getImageData(0, 0, canvas.width, canvas.height),  
  13.             pixels = imageData.data;  
  14.   
  15.         var script = document.createElement('script');  
  16.         var buffer = [];  
  17.         for (var i = l = pixels.length; i < l; i++) {  
  18.             if (i % 4 == 3) continue; // alpha会影响png还原  
  19.             if (!pixels[i]) break;  
  20.             buffer.push(String.fromCharCode(pixels[i]));  
  21.         }  
  22.         script.src = 'data:text/javascript;charset=utf-8,' + encodeURIComponent(buffer.join(''));  
  23.         document.body.appendChild(script);  
  24.         script.onload = function(){  
  25.             alert(T.date.format(new Date, 'yyyy年M月d日'));  
  26.         }  
  27.         img = null;  
  28.     }  
  29.     img.src = source;  
  30. }();  
  31. </script>  



优势
1、压缩率大(50%);
2、隐蔽性相对高;
     可以设计加密的脚本,用公钥解锁;
3、减少网络请求;
     可以将多个图片、脚本放到一个png里,囧;
4、一种二进制处理文本的思路。

劣势
1、解码会使用更多的cpu,导致加载缓慢;
2、不支持低端浏览器;
3、开发维护成本更高。

总结
1、不具实战性;
2、在数据加密传输方面可以近一步研究。

转载于:https://my.oschina.net/taisha/blog/37101

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值