起因
高階瀏覽器支援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。
解決方案就是:一個畫素存放三個字元並將alpha固定為255。
第二輪demo測試符合預期。
總結
build流程:
1、字串轉換成ascii碼;
2、建立足夠儲存空間的canvas;
3、將字元填入到畫素中(忽略alpha值);
4、獲取data url;
canvas.toDataURL(
"image/png"
);
5、存為png圖片。
程式碼示例
- <textarea id="base64"></textarea>
- <script>
- function encodeUTF8(str) {
- return String(str).replace(
- /[\u0080-\u07ff]/g,
- function(c) {
- var ccc = c.charCodeAt(0);
- return String.fromCharCode(0xc0 | cc >> 6, 0x80 | cc & 0x3f);
- }
- ).replace(
- /[\u0800-\uffff]/g,
- function(c) {
- var ccc = c.charCodeAt(0);
- return String.fromCharCode(0xe0 | cc >> 12, 0x80 | cc >> 6 & 0x3f, 0x80 | cc & 0x3f);
- }
- );
- }
- function request(url, loaded) {
- var xmlhttp = new XMLHttpRequest();
- xmlhttp.onreadystatechange = function() {
- if (xmlhttp.readyState == 4)
- if (xmlhttp.status == 200)
- loaded(xmlhttp);
- }
- xmlhttp.open("GET", url, true);
- xmlhttp.send();
- }
- void function(){
- var source = 'tangram-1.5.0.js';
- request(source, function(xmlhttp){
- var text = encodeUTF8(xmlhttp.responseText);
- var pixel = Math.ceil((text.length + 2) / 3); // 1一個畫素存3個位元組,
- var size = Math.ceil(Math.sqrt(pixel));
- //console.log([text.length, pixel, size, size * size * 3]);
- var canvas = document.createElement('canvas');
- canvascanvas.width = canvas.height = size;
- var context = canvas.getContext("2d"),
- imageData = context.getImageData(0, 0, canvas.width, canvas.height),
- pixels = imageData.data;
- for(var i = , j = , l = pixels.length; i < l; i++){
- if (i % 4 == 3) { // alpha會影響png還原
- pixels[i] = 255;
- continue;
- }
- var code = text.charCodeAt(j++);
- if (isNaN(code)) break;
- pixels[i] = code;
- }
- context.putImageData(imageData, 0, 0);
- document.getElementById('base64').value = canvas.toDataURL("image/png");
- });
- }();
- </script>
編譯結果
呼叫流程:
1、載入png;
2、將png原尺寸繪製到canvas中;
3、讀取畫素中的字串;
4、生成相應協議的data url使用。
程式碼示例
優勢
程式碼示例
- <script>
- void function(){
- var source = 'tangram-1.5.0.png';
- var img = document.createElement('img');
- img.onload = function(){
- var canvas = document.createElement('canvas');
- canvas.width = img.width;
- canvas.height = img.height;
- var context = canvas.getContext("2d");
- context.drawImage(img, 0, 0);
- var imageData = context.getImageData(0, 0, canvas.width, canvas.height),
- pixels = imageData.data;
- var script = document.createElement('script');
- var buffer = [];
- for (var i = , l = pixels.length; i < l; i++) {
- if (i % 4 == 3) continue; // alpha會影響png還原
- if (!pixels[i]) break;
- buffer.push(String.fromCharCode(pixels[i]));
- }
- script.src = 'data:text/javascript;charset=utf-8,' + encodeURIComponent(buffer.join(''));
- document.body.appendChild(script);
- script.onload = function(){
- alert(T.date.format(new Date, 'yyyy年M月d日'));
- }
- img = null;
- }
- img.src = source;
- }();
- </script>
優勢
1、壓縮率大(50%);
2、隱蔽性相對高;
可以設計加密的指令碼,用公鑰解鎖;
3、減少網路請求;
可以將多個圖片、指令碼放到一個png裡,囧;
4、一種二進位制處理文字的思路。
劣勢
1、解碼會使用更多的cpu,導致載入緩慢;
2、不支援低端瀏覽器;
3、開發維護成本更高。
總結
1、不具實戰性;
2、在資料加密傳輸方面可以近一步研究。