亚搏app官方网站上传并不帮助

选用 canvas 达成数据压缩

2016/03/15 · HTML5 · 1
评论 ·
Canvas

初稿出处:
EtherDream   

前言

HTTP 协助 GZip
压缩,可节约不计其数传输财富。但缺憾的是,唯有下载才有,上传并不扶助。

要是上传也能减小,这就全盘了。非常契合大批量文件提交的场面,举个例子腾讯网,正是很好的例证。

固然如此正规不扶助「上传压缩」,但还可以够友善来促成。

Flash

首选方案当然是 Flash,毕竟它提供了压缩 API。除了 zip 格式,还协助 lzma
这种一流压缩。

因为是原生接口,所以品质极高。何况对应的 swf 文件,也丰硕小。

JavaScript

Flash 逐步淘汰,但代表的 HTML5,却未曾提供压缩 API。只好本身用 JS
实现。

那即便平价,但运维速度就慢多了,何况相应的 JS 也比非常的大。

万一代码有 50kb,而数据压缩后只小 10kb,那就不足了。除非量大,才有意义。

其他

能不可能不要 JS,而是选拔一些接口,直接达成减少?

实则,在 HTML5 刚面世时,就专注到了二个意义:canvas 导出图片。可以扭转
jpg、png 等格式。

假如在思维的话,相信您也想到了。没有错,便是 png —— 它是无损压缩的。

咱俩把常备数据当成像素点,画到 canvas 上,然后导出成
png,便是贰个出奇的缩减包了~


下边伊始讨论。。。

数量转变

多少转像素,并不麻烦。1 个像素能够包容 4 个字节:

R = bytes[0] G = bytes[1] B = bytes[2] A = bytes[3]

1
2
3
4
R = bytes[0]
G = bytes[1]
B = bytes[2]
A = bytes[3]

骨子里有现有的艺术,可批量将数据填充成像素:

img = new ImageData(bytes, w, h); context.putImageData(img, w, h)

1
2
img = new ImageData(bytes, w, h);
context.putImageData(img, w, h)

但是,图片的宽高如何设定?

尺寸设定

最简便易行的,就是用 1px 的高度。比方有 一千 个像素,则填在 一千 x 1
的图纸里。

但一旦有 一千0 像素,就不可行了。因为 canvas 的尺寸,是有限定的。

不等的浏览器,最大尺寸不平等。有
4096 的,也有 32767 的。。。

以最大 4096 为例,如若老是都用那个增长幅度,分明不客观。

诸如有 n = 4100 个像素,大家选取 4096 x 2 的尺码:

| 1 | 2 | 3 | 4 | … | 4095 | 4096 | | 4097 | 4098 | 4099 | 4100 |
…… 未利用 ……

1
2
| 1    | 2    | 3    | 4    | …  | 4095 | 4096 |
| 4097 | 4098 | 4099 | 4100 | …… 未利用 ……

第二行只用到 4 个,剩下的 4092 个都空着了。

但 4100 = 41 * 100。如若用那个尺寸,就不会有浪费。

之所以,得对 n 分解因数:

n = w * h

1
n = w * h

如此那般就能够将 n 个像素,正好填满 w x h 的图片。

但 n
是质数的话,就无解了。这时浪费就不可幸免了,只是,怎么样技能浪费最少?

于是就成为那样五个难点:

什么用 n + m 个点,拼成二个 w x h 的矩形(0

设想到 MAX 十分小,穷举就足以。

我们遍历 h,总结相应的 w = ceil(n / h), 然后找寻最相仿 n 的 w * h。

var beg = Math.ceil(n / MAX); var end = Math.ceil(Math.sqrt(n)); var
minSize = 9e9; var bestH = 0, // 最后结出 bestW = 0; for (h = beg; h
end; h++) { var w = Math.ceil(n / h); var size = w * h; if (size
minSize) { minSize = size; bestW = w; bestH = h; } if (size == n) {
break; } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var beg = Math.ceil(n / MAX);
var end = Math.ceil(Math.sqrt(n));
 
var minSize = 9e9;
 
var bestH = 0,          // 最终结果
    bestW = 0;
 
for (h = beg; h  end; h++) {
    var w = Math.ceil(n / h);
    var size = w * h;
 
    if (size  minSize) {
        minSize = size;
        bestW = w;
        bestH = h;
    }
    if (size == n) {
        break;
    }
}

因为 w * h 和 h * w 是同样的,所以只需遍历到 sqrt(n) 就足以。

平等,也不必要从 1 开端,从 n / MAX 就能够。

那般,大家就能够找到最符合的图片尺寸。

自然,三番两次的空白像素,最后减掉后会非常的小。这一步其实并不如较重大。

渲染难点

定下尺寸,大家就足以「渲染数据」了。

可是现实中,总有个别意外的坑。canvas 也不例外:

亚搏app官方网站上传并不帮助。<canvas id=”canvas” width=”100″ heigth=”100″></canvas>
<script> var ctx = canvas.getContext(‘2d’); // 写入的数码 var
bytes = [100, 101, 102, 103]; var buf = new Uint8ClampedArray(bytes);
var img = new ImageData(buf, 1, 1); ctx.putImageData(img, 0, 0); //
读取的数目 img = ctx.getImageData(0, 0, 1, 1); console.log(img.data); //
chrome [亚搏app官方网站上传并不帮助。99, 102, 102, 103] // firefox [101, 101, 103, 103] // …
</script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<canvas id="canvas" width="100" heigth="100"></canvas>
<script>
  var ctx = canvas.getContext(‘2d’);
 
  // 写入的数据
  var bytes = [100, 101, 102, 103];
 
  var buf = new Uint8ClampedArray(bytes);
  var img = new ImageData(buf, 1, 1);
  ctx.putImageData(img, 0, 0);
 
  // 读取的数据
  img = ctx.getImageData(0, 0, 1, 1);
  console.log(img.data);
  // chrome  [99,  102, 102, 103]
  // firefox [101, 101, 103, 103]
  // …
</script>

读取的像素,居然和写入的有不是!并且分化的浏览器,偏差还不均等。

原本,浏览器为了进步渲染质量,有贰个 Premultiplied Alpha
的体制。不过,那会牺牲局地精度!

固然如此视觉上并不理解,但用于数据存款和储蓄,就有毛病了。

什么禁止使用它?一番品尝都没成功。于是,只可以从数额上镌刻了。

借使不选拔 Alpha 通道,又会怎样?

// 写入的多少 var bytes = [100, 101, 102, 255]; …
console.log(img.data); // [100, 101, 102, 255]

1
2
3
4
  // 写入的数据
  var bytes = [100, 101, 102, 255];
  …
  console.log(img.data);  // [100, 101, 102, 255]

如此,倒是避开了难点。

看来,只可以从数据上早先,跳过 Alpha 通道:

亚搏app官方网站上传并不帮助。// pixel 1 new_bytes[0] = bytes[0] // R new_bytes[1] =
bytes[1] // G new_亚搏app官方网站上传并不帮助。bytes[2] = bytes[2] // B new_bytes[3] = 255
// A // pixel 2 new_bytes[4] = bytes[3] // R new_bytes[5] =
bytes[4] // G new_bytes[6] = bytes[5] // B new_bytes[7] = 255
// A …

1
2
3
4
5
6
7
8
9
10
11
12
13
// pixel 1
new_bytes[0] = bytes[0]     // R
new_bytes[1] = bytes[1]     // G
new_bytes[2] = bytes[2]     // B
new_bytes[3] = 255          // A
 
// pixel 2
new_bytes[4] = bytes[3]     // R
new_bytes[5] = bytes[4]     // G
new_bytes[6] = bytes[5]     // B
new_bytes[7] = 255          // A
 

此时,就不受 Premultiplied Alpha 的震慑了。

由于轻便,也足以 1 像素存 1 字节:

// pixel 1 new_bytes[0] = bytes[0] new_bytes[1] = 255
new_bytes[2] = 255 new_bytes[3] = 255 // pixel 2 new_bytes[4] =
bytes[1] new_bytes[5] = 255 new_bytes[6] = 255 new_bytes[7] =
255 …

1
2
3
4
5
6
7
8
9
10
11
12
13
// pixel 1
new_bytes[0] = bytes[0]
new_bytes[1] = 255
new_bytes[2] = 255
new_bytes[3] = 255
 
// pixel 2
new_bytes[4] = bytes[1]
new_bytes[5] = 255
new_bytes[6] = 255
new_bytes[7] = 255
 

如此,整个图片最两唯有 256 色。要是能导出成「索引型
PNG」的话,也是能够尝试的。

数据编码

最终,便是将图像举办导出。

若是 canvas 能一贯导出成 blob,那是最佳的。因为 blob 可透过 AJAX 上传。

canvas.toBlob(function(blob) { // … }, ‘image/png’)

1
2
3
canvas.toBlob(function(blob) {
    // …
}, ‘image/png’)

不过,好多浏览器都不匡助。只可以导出 data uri 格式:

uri = canvas.toDataURL(‘image/png’) // data:image/png;base64,xxxx

1
uri = canvas.toDataURL(‘image/png’)  // data:image/png;base64,xxxx

但 base64 会大拉长度。所以,还得解回二进制:

base64 = uri.substr(uri.indexOf(‘,’) + 1) binary = atob(base64)

1
2
base64 = uri.substr(uri.indexOf(‘,’) + 1)
binary = atob(base64)

此时的 binary,就是最终数额了啊?

假使将 binary 通过 AJAX 提交的话,会发觉其实传输字节,比 binary.length
大。

原来 atob 再次来到的数码,仍是字符串型的。传输时,就提到字集编码了。

由此还需再调换叁回,产生真的的二进制数据:

var len = binary.length var buf = new Uint8Array(len) for (var i = 0; i
len; i++) { buf[i] = binary.charCodeAt(i) }

1
2
3
4
5
6
var len = binary.length
var buf = new Uint8Array(len)
 
for (var i = 0; i  len; i++) {
    buf[i] = binary.charCodeAt(i)
}

那会儿的 buf,才具被 AJAX 一点儿也不动的传输。

末段效果

总结,我们简要演示下:Demo

找二个大块的公文测验。举个例子 qq.com 首页 HTML,有 637,101 字节。

先选取「每像素 1 字节」的编码,各种浏览器生成的 PNG 大小:

Chrome FireFox Safari
体积 289,460 203,276 478,994
比率 45.4% 31.9% 75.2%

内部火狐压缩率最高,减弱了 2/3 的体积。

变迁的 PNG 看起来是这么的:

亚搏app官方网站 1

然则可惜的是,全部浏览器生成的图形,都不是「256 色索引」的。


再测验「每像素 3 字节」,看看会不会有改革:

Chrome FireFox Safari
体积 297,239 202,785 384,183
比率 46.7% 31.8% 60.3%

Safari 有了大多的上进,然而 Chrome 却更糟了。

FireFox 有些许的晋升,压缩率仍是最高的。

亚搏app官方网站 2

大同小异可惜的是,即便全体图片并未动用 Alpha 通道,但转换的 PNG 仍是 三17个人的。

同期,也无从设置压缩品级,使得这种压缩情势,作用并不高。

对待 Flash 压缩,差异就大概了:

deflate 压缩 lzma 压缩
体积 133,660 108,015
比率 21.0% 17.0%

并且 Flash 生成的是通用格式,后端解码时,使用规范库就可以。

而 PNG 还得位图解码、像素管理等手续,很劳累。

进而,现实中依旧预先使用 Flash,本文只是开脑洞而已。

实在用途

唯独这种办法,实际依然有效到过。用在叁个非常的大日志上传的场子(并且不能够用
Flash)。

因为后端并不解析,仅仅储存而已。所以,可以将日志对应的 PNG
下回地面,在总指挥本人Computer上解析。

解压更便于,正是将像素还原回数据,这里有个简陋的
Demo。

那样,既减弱了宽带,也节约存储空间。

3 赞 4 收藏 1
评论

亚搏app官方网站 3

发表评论

电子邮件地址不会被公开。 必填项已用*标注