欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android WebP 图片压缩与传输

程序员文章站 2024-03-07 17:50:57
1. 简介 直到4g时代,流量依然是宝贵的东西。而移动网络传输中,最占流量的一种载体:图片,成为了我们移动开发者不得不关注的一个问题。 我们关注的问题,无非是图片体积和...

1. 简介

直到4g时代,流量依然是宝贵的东西。而移动网络传输中,最占流量的一种载体:图片,成为了我们移动开发者不得不关注的一个问题。

我们关注的问题,无非是图片体积和质量如何达到一个比较和谐的平衡,希望得到质量不错的图片同时体积还不能太大。

走在时代前列的谷歌给出了一个不错的答案——webp。

webp是一种图片文件格式,在相同的压缩指标下,webp的有损压缩能比jpg小 25-34%。而在我自己的测试里,有时候能小50%。

2. 大企业背书

webp在2010年发布第一个版本,到现在已经6年了,谷歌旗下的各种网站g+、以及非常有代表性的youtube,他的视频文件格式webm就是基于webp构造的。

据说腾讯、淘宝、美团也有部分应用。

3. android 端 jpg 转换 webp

rxjava线程转换:

 string[] imgs = new string[]{"1.jpg", "2.jpg", "3.jpg", "4.jpg", "5.jpg"};
 string path = environment.getexternalstoragedirectory().getabsolutepath() + "/pictures/test/";
 @override
 protected void oncreate(bundle savedinstancestate) {
 super.oncreate(savedinstancestate);
 setcontentview(r.layout.activity_main);
// test = api.getbuilder().create(test.class);
 string[] permissions = {manifest.permission.write_external_storage
 , manifest.permission.read_phone_state
 , manifest.permission.camera};
 if (build.version.sdk_int >= build.version_codes.m) {
 requestpermissions(permissions, 0);
 }
 compress();
 }
 private void compress() {
 observable.from(imgs)
 .subscribeon(schedulers.io())
 .doonnext(new action1<string>() {
  @override
  public void call(string imgname) {
  compress(imgname);
  }
 })
 .subscribe();
 }
 private void compress(string imgname) {
 try {
 file file = new file(path, imgname);
 log.i("compress", "jpg start");
 byte[] bytes = bitmaputil.compressbitmaptobytes(file.getpath(), 600, 0, 60, bitmap.compressformat.jpeg);
 file jpg = new file(path, imgname + "compress.jpg");
 fileutils.writebytearraytofile(jpg, bytes);
 log.i("compress", "jpg finish");
 log.i("compress", "----------------------------------------------------");
 log.i("compress", "webp start");
 byte[] bytes1 = bitmaputil.compressbitmaptobytes(file.getpath(), 600, 0, 60, bitmap.compressformat.webp);//分别是图片路径,宽度高度,质量,和图片类型,重点在这里。
 file webp = new file(path, imgname + "compress.webp");
 fileutils.writebytearraytofile(webp, bytes1);
 log.i("compress", "webp finish");
 } catch (ioexception e) {
 e.printstacktrace();
 }
 }

我的测试机器也是oneplus 1 ,cm13,所以需要获取相应的权限。

利用rxjava来做线程操作,在io线程里做了耗时操作。

public static byte[] compressbitmaptobytes(string filepath, int reqwidth, int reqheight, int quality, bitmap.compressformat format) {
 bitmap bitmap = getsmallbitmap(filepath, reqwidth, reqheight);
 bytearrayoutputstream baos = new bytearrayoutputstream();
 bitmap.compress(format, quality, baos);
 byte[] bytes = baos.tobytearray();
 bitmap.recycle();
 log.i(tag, "bitmap compressed success, size: " + bytes.length);
 return bytes;
 }
 public static bitmap getsmallbitmap(string filepath, int reqwidth, int reqheight) {
 bitmapfactory.options options = new bitmapfactory.options();
 options.injustdecodebounds = true;
 bitmapfactory.decodefile(filepath, options);
 options.insamplesize = calculateinsamplesize(options, reqwidth, reqheight);
 options.injustdecodebounds = false;
// options.inpreferqualityoverspeed = true;
 return bitmapfactory.decodefile(filepath, options);
 }
 public static int calculateinsamplesize(bitmapfactory.options options, int reqwidth, int reqheight) {
 int h = options.outheight;
 int w = options.outwidth;
 int insamplesize = 0;
 if (h > reqheight || w > reqwidth) {
 float ratiow = (float) w / reqwidth;
 float ratioh = (float) h / reqheight;
 insamplesize = (int) math.min(ratioh, ratiow);
 }
 insamplesize = math.max(1, insamplesize);
 return insamplesize;
 }

根据输入的宽高值计算分辨率的缩小比例,再根据输入的压缩质量数值,压缩图片,获得压缩后bitmap,然后再将其保存成本地文件。

这是非常常见的图片压缩手段。

4. webp对比

我用我日常生活里拍下的照片来做简单的对比测试,不是特别严谨,仅供简单的参考。

拍照设备是刷了cm13的 一加 1 。拍照场景都是日常生活特别常见的。

以下是原图预览,就不一个个放出来了,太大。

Android WebP 图片压缩与传输

缩小分辨率,同时压缩质量

文件名 照片原图 压缩后jpg 压缩后webp 压缩比
1.jpg 5760 kb 98 kb 74 kb 24.49%
2.jpg 4534 kb 64 kb 35 kb 45.31%
3.jpg 4751 kb 93 kb 68 kb 26.88%
4.jpg 7002 kb 121 kb 95 kb 21.49%
5.jpg 5493 kb 111 kb 91 kb 18.02%

平均压缩比是:27.24%

按照原图大小,不缩小分辨率,仅压缩质量。

文件名 照片原图 压缩后jpg 压缩后webp 压缩比
3.jpg 4751 kb 796 kb 426 kb 46.48%

至此,我们就非常方便的使用了webp来对图片进行更加极致的压缩,兼顾了图片体积和质量。

呃,csdn不支持上传webp的图片。你们可以看压缩包。
我嫌麻烦,可能不会传压缩包了……所以,你们看截图吧~

睁大眼睛对比一下有啥区别,不缩小分辨率,仅压缩质量,这个3.jpg可是有46.48%的压缩比噢。
这个场景是晚上在灯光充足的室内吃饭拍的。

Android WebP 图片压缩与传输

Android WebP 图片压缩与传输

5. 用gzip再压缩

刚刚是针对本地图片的压缩,接下来,我们需要将图片传输到服务器。这个过程依然有优化空间,就是利用gzip。

gzip的作用对象是整个请求体,具体来说是对请求体中的内容进行可逆的压缩,类似pc上zip的那种。

gzip压缩的请求体,需要加入相应的header: 「content-encoding:gzip」。
这事情retrofit会帮你做好。

后台服务器接收到在此类型的请求,就会对请求体解压,因此需要后端的支持。

另外要注意的是,gzip针对比较大的请求体压缩效果不错,尤其是未经过压缩的纯文本类型。

如果请求本来就很小,那么就不要使用gzip压缩了,压缩包自己的元数据可能比你的请求体还大,得不偿失。你可以自己测试一下,我估计zip和gzip的压缩字典比较类似,可以直接在pc上做测试。

6. retrofit对请求gzip压缩

网络请求方面,我项目里使用retrofit (okhttp) + rxjava。

retrofit的gzip压缩,本质上是通过okhttp的拦截器来完成的。

0拦截请求
1加入header
2压缩请求
3发送出去

搞定,方便。

https://github.com/square/okhttp/wiki/interceptors

/** this interceptor compresses the http request body. many webservers can't handle this! */
final class gziprequestinterceptor implements interceptor {
 @override public response intercept(interceptor.chain chain) throws ioexception {
 request originalrequest = chain.request();
 if (originalrequest.body() == null || originalrequest.header("content-encoding") != null) {
 return chain.proceed(originalrequest);
 }
 request compressedrequest = originalrequest.newbuilder()
 .header("content-encoding", "gzip")
 .method(originalrequest.method(), gzip(originalrequest.body()))
 .build();
 return chain.proceed(compressedrequest);
 }
 private requestbody gzip(final requestbody body) {
 return new requestbody() {
 @override public mediatype contenttype() {
 return body.contenttype();
 }
 @override public long contentlength() {
 return -1; // we don't know the compressed length in advance!
 }
 @override public void writeto(bufferedsink sink) throws ioexception {
 bufferedsink gzipsink = okio.buffer(new gzipsink(sink));
 body.writeto(gzipsink);
 gzipsink.close();
 }
 };
 }
}

7. 请求体抓包对比

我会用fiddler4 监测整个请求过程,以方便我们得知实际传输了多大的数据。

上传的具体代码就不发了,这个不是重点。

我把请求抓包之后,把request这个保存下来。

Android WebP 图片压缩与传输

这是同时上传两张图片的大小

文件名 请求体大小
webp+gzip 169kb
webp 222kb

用gzip压缩 比不加gzip 又小 23% ! jpg我就不发了,可以按照前面的估算一下~

webp比jpg小27%,然后gzip+webp又比单纯的webp小23%,节省下来的流量多可观啊!

8. 最后

webp默认只支持android 4.0以上,现在项目最低支持的版本是16,所以没什么问题。如果你的项目最低要支持到2.0,好像也有第三方支持,但是我建议你抓产品出去打一顿。

在我们的项目里,ios没用使用该技术。

根据下面的参考链接的数据以及我本人测试数据来看,webp不仅大大的节省了用户的流量,同时还可以加速图片传输速度。就照片传输的角度来看,是非常有利的。

如果还是有人告诉你:“ios做不了,不做了。”,“后台xxx不做了。”

我建议你抓产品出去打一顿。

相关参考:

http://isux.tencent.com/introduction-of-webp.html(产品经理看这个)

http://blog.csdn.net/geeklei/article/details/41147479 (后台需要看这个)

https://developers.google.com/speed/webp/

http://www.infoq.com/cn/articles/sdk-optimazation?utm_campaign=infoq_content&utm_source=infoq&utm_medium=feed&utm_term (他们表示ios也没有用上)

http://blog.csdn.net/mingchunhu/article/details/8155742(android全部都要看)