当前位置: 永利皇宫463手机版 > Web前端 > 正文

前端实现,实现数据压缩

时间:2019-10-06 20:01来源:Web前端
利用 canvas 实现数据压缩 2016/03/15 · HTML5 · 1评论 ·Canvas 原文出处:EtherDream    前端实现 SVG 转 PNG 2015/11/16 · JavaScript· PNG,SVG 原文出处: 百度FEX -zhangbobell    前言 HTTP 支持 GZip压缩,

利用 canvas 实现数据压缩

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

原文出处: EtherDream   

前端实现 SVG 转 PNG

2015/11/16 · JavaScript · PNG, SVG

原文出处: 百度FEX - zhangbobell   

前言

HTTP 支持 GZip 压缩,可节省不少传输资源。但遗憾的是,只有下载才有,上传并不支持。

如果上传也能压缩,那就完美了。特别适合大量文本提交的场合,比如博客园,就是很好的例子。

虽然标准不支持「上传压缩」,但仍可以自己来实现。

前言

svg 是一种矢量图形,在 web 上应用很广泛,但是很多时候由于应用的场景,常常需要将 svg 转为 png 格式,下载到本地等。随着浏览器对 HTML 5 的支持度越来越高,我们可以把 svg 转为 png 的工作交给浏览器来完成。

Flash

首选方案当然是 Flash,毕竟它提供了压缩 API。除了 zip 格式,还支持 lzma 这种超级压缩。

因为是原生接口,所以性能极高。而且对应的 swf 文件,也非常小。

一般方式

  1. 创建 imageimage,src = xxx.svg;
  2. 创建 canvas,dragImage 将图片贴到 canvas 上;
  3. 利用 toDataUrl 函数,将 canvas 的表示为 url;
  4. new image, src = url, download = download.png;

但是,在转换的时候有时有时会碰到如下的如下的两个问题:

JavaScript

Flash 逐渐淘汰,但取而代之的 HTML5,却没有提供压缩 API。只能自己用 JS 实现。

这虽然可行,但运行速度就慢多了,而且相应的 JS 也很大。

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

问题 1 :浏览器对 canvas 限制

Canvas 的 W3C 的标准上没有提及 canvas 的最大高/宽度和面积,但是每个厂商的浏览器出于浏览器性能的考虑,在不同的平台上设置了最大的高/宽度或者是渲染面积,超过了这个阈值渲染的结果会是空白。测试了几种浏览器的 canvas 性能如下:

  • chrome (版本 46.0.2490.80 (64-bit))
    • 最大面积:268, 435, 456 px^2 = 16, 384 px * 16, 384 px
    • 最大宽/高:32, 767 px
  • firefox (版本 42.0)
    • 最大面积:32, 767 px * 16, 384 px
    • 最大宽/高:32, 767px
  • safari (版本 9.0.1 (11601.2.7.2))
    • 最大面积: 268, 435, 456 px^2 = 16, 384 px * 16, 384 px
  • ie 10(版本 10.0.9200.17414)
    • 最大宽/高: 8, 192px * 8, 192px

在一般的 web 应用中,可能很少会超过这些限制。但是,如果超过了这些限制,则 会导致导出为空白或者由于内存泄露造成浏览器崩溃。

而且从另一方面来说, 导出 png 也是一项很消耗内存的操作,粗略估算一下,导出 16, 384 px * 16, 384 px 的 svg 会消耗 16384 * 16384 * 4 / 1024 / 1024 = 1024 M 的内存。所以,在接近这些极限值的时候,浏览器也会 反应变慢,能否导出成功也跟系统的可用内存大小等等都有关系。

对于这个问题,有如下两种解决方法:

  1. 将数据发送给后端,在后端完成 转换;
  2. 前端将 svg 切分成多个图片导出;

第一种方法可以使用 PhantomJS、inkscape、ImageMagick 等工具,相对来说比较简单,这里我们主要探讨第二种解决方法。

其他

能否不用 JS,而是利用某些接口,间接实现压缩?

事实上,在 HTML5 刚出现时,就注意到了一个功能:canvas 导出图片。可以生成 jpg、png 等格式。

如果在思考的话,相信你也想到了。没错,就是 png —— 它是无损压缩的。

我们把普通数据当成像素点,画到 canvas 上,然后导出成 png,就是一个特殊的压缩包了~


下面开始探索。。。

svg 切分成多个图片导出

思路:浏览器虽然对 canvas 有尺寸和面积的限制,但是对于 image 元素并没有明确的限制,也就是第一步生成的 image 其实显示是正常的,我们要做的只是在第二步 dragImage 的时候分多次将 image 元素切分并贴到 canvas 上然后下载下来。 同时,应注意到 image 的载入是一个异步的过程。

关键代码

JavaScript

// 构造 svg Url,此处省略将 svg 经字符过滤后转为 url 的过程。 var svgUrl = DomURL.createObjectURL(blob); var svgWidth = document.querySelector('#kity_svg').getAttribute('width'); var svgHeight = document.querySelector('#kity_svg').getAttribute('height'); // 分片的宽度和高度,可根据浏览器做适配 var w0 = 8192; var h0 = 8192; // 每行和每列能容纳的分片数 var M = Math.ceil(svgWidth / w0); var N = Math.ceil(svgHeight / h0); var idx = 0; loadImage(svgUrl).then(function(img) { while(idx < M * N) { // 要分割的面片在 image 上的坐标和尺寸 var targetX = idx % M * w0, targetY = idx / M * h0, targetW = (idx + 1) % M ? w0 : (svgWidth - (M - 1) * w0), targetH = idx >= (N - 1) * M ? (svgHeight - (N - 1) * h0) : h0; var canvas = document.createElement('canvas'), ctx = canvas.getContext('2d'); canvas.width = targetW; canvas.height = targetH; ctx.drawImage(img, targetX, targetY, targetW, targetH, 0, 0, targetW, targetH); console.log('now it is ' + idx); // 准备在前端下载 var a = document.createElement('a'); a.download = 'naotu-' + idx + '.png'; a.href = canvas.toDataURL('image/png'); var clickEvent = new MouseEvent('click', { 'view': window, 'bubbles': true, 'cancelable': false }); a.dispatchEvent(clickEvent); idx++; } }, function(err) { console.log(err); }); // 加载 image function loadImage(url) { return new Promise(function(resolve, reject) { var image = new Image(); image.src = url; image.crossOrigin = 'Anonymous'; image.onload = function() { resolve(this); }; image.onerror = function(err) { reject(err); }; }); }

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 构造 svg Url,此处省略将 svg 经字符过滤后转为 url 的过程。
var svgUrl = DomURL.createObjectURL(blob);
var svgWidth = document.querySelector('#kity_svg').getAttribute('width');
var svgHeight = document.querySelector('#kity_svg').getAttribute('height');
 
// 分片的宽度和高度,可根据浏览器做适配
var w0 = 8192;
var h0 = 8192;
 
// 每行和每列能容纳的分片数
var M = Math.ceil(svgWidth / w0);
var N = Math.ceil(svgHeight / h0);
 
var idx = 0;
loadImage(svgUrl).then(function(img) {
 
    while(idx < M * N) {
        // 要分割的面片在 image 上的坐标和尺寸
        var targetX = idx % M * w0,
            targetY = idx / M * h0,
            targetW = (idx + 1) % M ? w0 : (svgWidth - (M - 1) * w0),
            targetH = idx >= (N - 1) * M ? (svgHeight - (N - 1) * h0) : h0;
 
        var canvas = document.createElement('canvas'),
            ctx = canvas.getContext('2d');
 
            canvas.width = targetW;
            canvas.height = targetH;
 
            ctx.drawImage(img, targetX, targetY, targetW, targetH, 0, 0, targetW, targetH);
 
            console.log('now it is ' + idx);
 
            // 准备在前端下载
            var a = document.createElement('a');
            a.download = 'naotu-' + idx + '.png';
            a.href = canvas.toDataURL('image/png');
 
            var clickEvent = new MouseEvent('click', {
                'view': window,
                'bubbles': true,
                'cancelable': false
            });
 
            a.dispatchEvent(clickEvent);
 
        idx++;
    }
 
}, function(err) {
    console.log(err);
});
 
// 加载 image
function loadImage(url) {
    return new Promise(function(resolve, reject) {
        var image = new Image();
 
        image.src = url;
        image.crossOrigin = 'Anonymous';
        image.onload = function() {
            resolve(this);
        };
 
        image.onerror = function(err) {
            reject(err);
        };
    });
}

说明:

  1. 由于在前端下载有浏览器兼容性、用户体验等问题,在实际中,可能需要将生成后的数据发送到后端,并作为一个压缩包下载。
  2. 分片的尺寸这里使用的是 8192 * 9192,在实际中,为了增强兼容性和体验,可以根据浏览器和平台做适配,例如在 iOS 下的 safari 的最大面积是 4096 *4096。

数据转换

数据转像素,并不麻烦。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)

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

问题 2 :导出包含图片的 svg

在导出的时候,还会碰到另一个问题:如果 svg 里面包含图片,你会发现通过以上方法导出的 png 里面,原来的图片是不显示的。一般认为是 svg 里面包含的图片跨域了,但是如果你把这个图片换成本域的图片,还是会出现这种情况。图片 1

图片中上部分是导出前的 svg,下图是导出后的 png。svg 中的图片是本域的,在导出后不显示。

尺寸设定

最简单的,就是用 1px 的高度。比如有 1000 个像素,则填在 1000 x 1 的图片里。

但如果有 10000 像素,就不可行了。因为 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 即可。

这样,我们就能找到最适合的图片尺寸。

当然,连续的空白像素,最终压缩后会很小。这一步其实并不特别重要。

问题来源

我们按照文章最开始提出的步骤,逐步排查,会发现在第一步的时候,svg 中的图片就不显示了。也就是,当 image 元素的 src 为一个 svg,并且 svg 里面包含图片,那么被包含的图片是不会显示的,即使这个图片是本域的。

W3C 关于这个问题并没有 做说明,最后在  找到了关于这个问题的说明。 意思是:禁止这么做是出于安全考虑,svg 里面引用的所有 外部资源 包括 image, stylesheet, script 等都会被阻止。

里面还举了一个例子:假设没有这个限制,如果一个论坛允许用户上传这样的 svg 作为头像,就有可能出现这样的场景,一位黑客上传 svg 作为头像,里面包含代码:<image xlink:href="http://evilhacker.com/myimage.png">(假设这位黑客拥有对于 evilhacker.com 的控制权),那么这位黑客就完全能做到下面的事情:

  • 只要有人查看他的资料,evilhacker.com 就会接收到一次 ping 的请求(进而可以拿到查看者的 ip);
  • 可以做到对于不同的 ip 地址的人展示不一样的头像;
  • 可以随时更换头像的外观(而不用通过论坛管理员的审核)。

看到这里,大概就明白了整个问题的来龙去脉了,当然还有一点原因可能是避免图像递归。

渲染问题

定下尺寸,我们就可以「渲染数据」了。

然而现实中,总有些意想不到的坑。canvas 也不例外:

<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>

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 通道:

// 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 ...

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」的话,也是可以尝试的。

解决办法

思路:由于安全因素,其实第一步的时候,图片已经显示不出来了。那么我们现在考虑的方法是在第一步之后遍历 svg 的结构,将所有的 image 元素的 url、位置和尺寸保存下来。在第三步之后,按顺序贴到 canvas 上。这样,最后导出的 png 图片就会有 svg 里面的 image。关键代码

JavaScript

// 此处略去生成 svg url 的过程 var svgUrl = DomURL.createObjectURL(blob); var svgWidth = document.querySelector('#kity_svg').getAttribute('width'); var svgHeight = document.querySelector('#kity_svg').getAttribute('height'); var embededImages = document.querySelectorAll('#kity_svg image'); // 由 nodeList 转为 array embededImages = Array.prototype.slice.call(embededImages); // 加载底层的图 loadImage(svgUrl).then(function(img) { var canvas = document.createElement('canvas'), ctx = canvas.getContext("2d"); canvas.width = svgWidth; canvas.height = svgHeight; ctx.drawImage(img, 0, 0); // 遍历 svg 里面所有的 image 元素 embededImages.reduce(function(sequence, svgImg){ return sequence.then(function() { var url = svgImg.getAttribute('xlink:href') + 'abc', dX = svgImg.getAttribute('x'), dY = svgImg.getAttribute('y'), dWidth = svgImg.getAttribute('width'), dHeight = svgImg.getAttribute('height'); return loadImage(url).then(function( sImg) { ctx.drawImage(sImg, 0, 0, sImg.width, sImg.height, dX, dY, dWidth, dHeight); }, function(err) { console.log(err); }); }, function(err) { console.log(err); }); }, Promise.resolve()).then(function() { // 准备在前端下载 var a = document.createElement("a"); a.download = 'download.png'; a.href = canvas.toDataURL("image/png"); var clickEvent = new MouseEvent("click", { "view": window, "bubbles": true, "cancelable": false }); a.dispatchEvent(clickEvent); }); }, function(err) { console.log(err); }) // 省略了 loadImage 函数 // 代码和第一个例子相同

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
56
57
58
// 此处略去生成 svg url 的过程
var svgUrl = DomURL.createObjectURL(blob);
var svgWidth = document.querySelector('#kity_svg').getAttribute('width');
var svgHeight = document.querySelector('#kity_svg').getAttribute('height');
 
var embededImages = document.querySelectorAll('#kity_svg image');
// 由 nodeList 转为 array
embededImages = Array.prototype.slice.call(embededImages);
// 加载底层的图
loadImage(svgUrl).then(function(img) {
 
var canvas = document.createElement('canvas'),
ctx = canvas.getContext("2d");
 
canvas.width = svgWidth;
canvas.height = svgHeight;
 
ctx.drawImage(img, 0, 0);
    // 遍历 svg 里面所有的 image 元素
    embededImages.reduce(function(sequence, svgImg){
 
        return sequence.then(function() {
            var url = svgImg.getAttribute('xlink:href') + 'abc',
                dX = svgImg.getAttribute('x'),
                dY = svgImg.getAttribute('y'),
                dWidth = svgImg.getAttribute('width'),
                dHeight = svgImg.getAttribute('height');
 
            return loadImage(url).then(function( sImg) {
                ctx.drawImage(sImg, 0, 0, sImg.width, sImg.height, dX, dY, dWidth, dHeight);
            }, function(err) {
                console.log(err);
            });
        }, function(err) {
            console.log(err);
        });
    }, Promise.resolve()).then(function() {
        // 准备在前端下载
        var a = document.createElement("a");
        a.download = 'download.png';
        a.href = canvas.toDataURL("image/png");
 
        var clickEvent = new MouseEvent("click", {
            "view": window,
            "bubbles": true,
            "cancelable": false
        });
 
        a.dispatchEvent(clickEvent);
 
        });
 
      }, function(err) {
        console.log(err);
   })
 
   // 省略了 loadImage 函数
   // 代码和第一个例子相同

说明

  1. 例子中 svg 里面的图像是根节点下面的,因此用于表示位置的 x, y 直接取来即可使用,在实际中,这些位置可能需要跟其他属性做一些运算之后得出。如果是基于 svg 库构建的,那么可以直接使用库里面用于定位的函数,比直接从底层运算更加方便和准确。
  2. 我们这里讨论的是本域的图片的导出问题,跨域的图片由于「污染了」画布,在执行 toDataUrl 函数的时候会报错。

数据编码

最后,就是将图像进行导出。

如果 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 原封不动的传输。

结语

在这里和大家分享了 在前端将 svg 转为 png 的方法和过程中可能会遇到的两个问题,一个是浏览器对 canvas 的尺寸限制,另一个是导出图片的问题。当然,这两个问题还有其他的解决方法,同时由于知识所限,本文内容难免有纰漏,欢迎大家批评指正。最后感谢@techird 和 @Naxior 关于这两个问题的讨论。

1 赞 2 收藏 评论

图片 2

最终效果

综上所述,我们简单演示下: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 看起来是这样的:

图片 3

不过遗憾的是,所有浏览器生成的图片,都不是「256 色索引」的。


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

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

Safari 有了不少的进步,不过 Chrome 却更糟了。

FireFox 有略微的提升,压缩率仍是最高的。

图片 4

同样遗憾的是,即使整个图片并没有用到 Alpha 通道,但生成的 PNG 仍是 32 位的。

而且,也无法设置压缩等级,使得这种压缩方式,效率并不高。

相比 Flash 压缩,差距就大多了:

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

并且 Flash 生成的是通用格式,后端解码时,使用标准库即可。

而 PNG 还得位图解码、像素处理等步骤,很麻烦。

所以,现实中还是优先使用 Flash,本文只是开脑洞而已。

实际用途

不过这种方式,实际还是有用到过。用在一个较大日志上传的场合(并且不能用 Flash)。

因为后端并不分析,仅仅储存而已。所以,可以将日志对应的 PNG 下回本地,在管理员自己电脑上解析。

解压更容易,就是将像素还原回数据,这里有个简陋的 Demo。

这样,既减少了宽带,也节省存储空间。

3 赞 4 收藏 1 评论

图片 5

编辑:Web前端 本文来源:前端实现,实现数据压缩

关键词:

  • 上一篇:没有了
  • 下一篇:没有了