Android 端 WebP 圖片壓縮與傳輸的一點探索
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 。拍照場景都是日常生活特別常見的。
以下是原圖預覽,就不一個個放出來了,太大。
縮小解析度,同時壓縮質量
檔名 | 照片原圖 | 壓縮後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%的壓縮比噢。
這個場景是晚上在燈光充足的室內吃飯拍的。
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這個儲存下來。
這是同時上傳兩張圖片的大小
檔名 | 請求體大小 |
---|---|
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全部都要看)
相關文章
- 最佳化部落格Ⅰ-壓縮圖片為webp格式Web
- 淺談移動端圖片壓縮(iOS & Android)iOSAndroid
- js上傳圖片壓縮JS
- 前端的圖片壓縮image-compressor(可在圖片上傳前實現圖片壓縮)前端
- 探索HTTP傳輸中gzip壓縮的祕密HTTP
- vue 上傳圖片進行壓縮圖片Vue
- 前端圖片壓縮及上傳前端
- 一個Vue圖片上傳剪裁壓縮元件Vue元件
- 資料壓縮傳輸與斷點續傳那些事兒斷點
- JS—圖片壓縮上傳(單張)JS
- 移動端使用localResizeIMG4壓縮圖片並上傳
- Android-壓縮大圖到容量超小的圖片Android
- ionic3 上傳圖片、壓縮圖片到阿里雲 服務端簽名後直傳阿里服務端
- Android-圖片壓縮(二)-純乾貨Android
- WebP影像格式的原理 與影像壓縮的關係Web
- layui中實現上傳圖片壓縮UI
- 前端圖片壓縮 - H5&Uni-App圖片壓縮前端H5APP
- Nginx網路壓縮 CSS壓縮 圖片壓縮 JSON壓縮NginxCSSJSON
- ??圖片壓縮CanvasCanvas
- canvas 壓縮圖片Canvas
- 圖片壓縮20201109
- 直播app開發搭建,Android studio 圖片壓縮APPAndroid
- 基於vue + axios + lrz.js 微信端圖片壓縮上傳VueiOSJS
- python 開發一款圖片壓縮工具(四):上傳圖床Python圖床
- android短視訊開發,呼叫相機、相簿,壓縮圖片後上傳Android
- Glide 原始碼分析(一):圖片壓縮IDE原始碼
- iOS 圖片壓縮方法iOS
- 前端圖片壓縮方案前端
- Photoshop壓縮png圖片
- 圖片壓縮怎樣操作?分享幾種實用的批次圖片壓縮技巧
- 在瀏覽器端用H5實現圖片壓縮上傳瀏覽器H5
- PbootCMS上傳圖片被壓縮怎麼解決boot
- 基於 NSData 的圖片壓縮
- Bitmap的圖片壓縮彙總
- excel檔案裡的圖片怎麼壓縮?excel檔案裡圖片的壓縮方法Excel
- 如何使用FormData上傳壓縮裁剪後的圖片Blob物件ORM物件
- 影像體積壓縮工具JPEG Jackal更好的壓縮圖片
- 使用tinypng對需要上傳Gitee圖床的圖片進行壓縮Gitee圖床
- java,springboot + thymeleaf 上傳圖片、刪除圖片到伺服器、本地,壓縮圖片上傳(有些圖片會失真),原圖上傳JavaSpring Boot伺服器