Android圖片壓縮(質量壓縮和尺寸壓縮)&Bitmap轉成字符串上傳

在網上調查了圖片壓縮的方法並實裝後,大致上可以認爲有兩類壓縮:質量壓縮(不改變圖片的尺寸)和尺寸壓縮(相當於是像素上的壓縮);質量壓縮一般可用於上傳大圖前的處理,這樣就可以節省一定的流量,畢竟現在的手機拍照都能達到3M左右了,尺寸壓縮一般可用於生成縮略圖。
兩種方法都實裝在了我的項目中,結果卻發現在質量壓縮的模塊中,本來1.9M的圖片壓縮後反而變成3M多了,很是奇怪,再做了進一步調查終於知道原因了。下面這個博客說的比較清晰:

android圖片壓縮總結

總結來看,圖片有三種存在形式:硬盤上時是file,網絡傳輸時是stream,內存中是stream或bitmap,所謂的質量壓縮,它其實只能實現對file的影響,你可以把一個file轉成bitmap再轉成file,或者直接將一個bitmap轉成file時,這個最終的file是被壓縮過的,但是中間的bitmap並沒有被壓縮(或者說幾乎沒有被壓縮,我不確定),因爲bigmap在內存中的大小是按像素計算的,也就是width * height,對於質量壓縮,並不會改變圖片的像素,所以就算質量被壓縮了,但是bitmap在內存的佔有率還是沒變小,但你做成file時,它確實變小了;

而尺寸壓縮由於是減小了圖片的像素,所以它直接對bitmap產生了影響,當然最終的file也是相對的變小了;

最後把自己總結的工具類貼出來:
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. import java.io.ByteArrayInputStream;  
  2. import java.io.ByteArrayOutputStream;  
  3. import java.io.File;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7.   
  8. import android.graphics.Bitmap;  
  9. import android.graphics.Bitmap.Config;  
  10. import android.graphics.BitmapFactory;  
  11.   
  12. /** 
  13.  * Image compress factory class 
  14.  *  
  15.  * @author  
  16.  * 
  17.  */  
  18. public class ImageFactory {  
  19.   
  20.     /** 
  21.      * Get bitmap from specified image path 
  22.      *  
  23.      * @param imgPath 
  24.      * @return 
  25.      */  
  26.     public Bitmap getBitmap(String imgPath) {  
  27.         // Get bitmap through image path  
  28.         BitmapFactory.Options newOpts = new BitmapFactory.Options();  
  29.         newOpts.inJustDecodeBounds = false;  
  30.         newOpts.inPurgeable = true;  
  31.         newOpts.inInputShareable = true;  
  32.         // Do not compress  
  33.         newOpts.inSampleSize = 1;  
  34.         newOpts.inPreferredConfig = Config.RGB_565;  
  35.         return BitmapFactory.decodeFile(imgPath, newOpts);  
  36.     }  
  37.       
  38.     /** 
  39.      * Store bitmap into specified image path 
  40.      *  
  41.      * @param bitmap 
  42.      * @param outPath 
  43.      * @throws FileNotFoundException  
  44.      */  
  45.     public void storeImage(Bitmap bitmap, String outPath) throws FileNotFoundException {  
  46.         FileOutputStream os = new FileOutputStream(outPath);  
  47.         bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);  
  48.     }  
  49.       
  50.     /** 
  51.      * Compress image by pixel, this will modify image width/height.  
  52.      * Used to get thumbnail 
  53.      *  
  54.      * @param imgPath image path 
  55.      * @param pixelW target pixel of width 
  56.      * @param pixelH target pixel of height 
  57.      * @return 
  58.      */  
  59.     public Bitmap ratio(String imgPath, float pixelW, float pixelH) {  
  60.         BitmapFactory.Options newOpts = new BitmapFactory.Options();    
  61.         // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true,即只讀邊不讀內容  
  62.         newOpts.inJustDecodeBounds = true;  
  63.         newOpts.inPreferredConfig = Config.RGB_565;  
  64.         // Get bitmap info, but notice that bitmap is null now    
  65.         Bitmap bitmap = BitmapFactory.decodeFile(imgPath,newOpts);  
  66.             
  67.         newOpts.inJustDecodeBounds = false;    
  68.         int w = newOpts.outWidth;    
  69.         int h = newOpts.outHeight;    
  70.         // 想要縮放的目標尺寸  
  71.         float hh = pixelH;// 設置高度爲240f時,可以明顯看到圖片縮小了  
  72.         float ww = pixelW;// 設置寬度爲120f,可以明顯看到圖片縮小了  
  73.         // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可    
  74.         int be = 1;//be=1表示不縮放    
  75.         if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放    
  76.             be = (int) (newOpts.outWidth / ww);    
  77.         } else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放    
  78.             be = (int) (newOpts.outHeight / hh);    
  79.         }    
  80.         if (be <= 0) be = 1;    
  81.         newOpts.inSampleSize = be;//設置縮放比例  
  82.         // 開始壓縮圖片,注意此時已經把options.inJustDecodeBounds 設回false了  
  83.         bitmap = BitmapFactory.decodeFile(imgPath, newOpts);  
  84.         // 壓縮好比例大小後再進行質量壓縮  
  85. //        return compress(bitmap, maxSize); // 這裏再進行質量壓縮的意義不大,反而耗資源,刪除  
  86.         return bitmap;  
  87.     }  
  88.       
  89.     /** 
  90.      * Compress image by size, this will modify image width/height.  
  91.      * Used to get thumbnail 
  92.      *  
  93.      * @param image 
  94.      * @param pixelW target pixel of width 
  95.      * @param pixelH target pixel of height 
  96.      * @return 
  97.      */  
  98.     public Bitmap ratio(Bitmap image, float pixelW, float pixelH) {  
  99.         ByteArrayOutputStream os = new ByteArrayOutputStream();  
  100.         image.compress(Bitmap.CompressFormat.JPEG, 100, os);  
  101.         if( os.toByteArray().length / 1024>1024) {//判斷如果圖片大於1M,進行壓縮避免在生成圖片(BitmapFactory.decodeStream)時溢出      
  102.             os.reset();//重置baos即清空baos    
  103.             image.compress(Bitmap.CompressFormat.JPEG, 50, os);//這裏壓縮50%,把壓縮後的數據存放到baos中    
  104.         }    
  105.         ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());    
  106.         BitmapFactory.Options newOpts = new BitmapFactory.Options();    
  107.         //開始讀入圖片,此時把options.inJustDecodeBounds 設回true了    
  108.         newOpts.inJustDecodeBounds = true;  
  109.         newOpts.inPreferredConfig = Config.RGB_565;  
  110.         Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);    
  111.         newOpts.inJustDecodeBounds = false;    
  112.         int w = newOpts.outWidth;    
  113.         int h = newOpts.outHeight;    
  114.         float hh = pixelH;// 設置高度爲240f時,可以明顯看到圖片縮小了  
  115.         float ww = pixelW;// 設置寬度爲120f,可以明顯看到圖片縮小了  
  116.         //縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可    
  117.         int be = 1;//be=1表示不縮放    
  118.         if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放    
  119.             be = (int) (newOpts.outWidth / ww);    
  120.         } else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放    
  121.             be = (int) (newOpts.outHeight / hh);    
  122.         }    
  123.         if (be <= 0) be = 1;    
  124.         newOpts.inSampleSize = be;//設置縮放比例    
  125.         //重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了    
  126.         is = new ByteArrayInputStream(os.toByteArray());    
  127.         bitmap = BitmapFactory.decodeStream(is, null, newOpts);  
  128.         //壓縮好比例大小後再進行質量壓縮  
  129. //      return compress(bitmap, maxSize); // 這裏再進行質量壓縮的意義不大,反而耗資源,刪除  
  130.         return bitmap;  
  131.     }  
  132.       
  133.     /** 
  134.      * Compress by quality,  and generate image to the path specified 
  135.      *  
  136.      * @param image 
  137.      * @param outPath 
  138.      * @param maxSize target will be compressed to be smaller than this size.(kb) 
  139.      * @throws IOException  
  140.      */  
  141.     public void compressAndGenImage(Bitmap image, String outPath, int maxSize) throws IOException {  
  142.         ByteArrayOutputStream os = new ByteArrayOutputStream();  
  143.         // scale  
  144.         int options = 100;  
  145.         // Store the bitmap into output stream(no compress)  
  146.         image.compress(Bitmap.CompressFormat.JPEG, options, os);    
  147.         // Compress by loop  
  148.         while ( os.toByteArray().length / 1024 > maxSize) {  
  149.             // Clean up os  
  150.             os.reset();  
  151.             // interval 10  
  152.             options -= 10;  
  153.             image.compress(Bitmap.CompressFormat.JPEG, options, os);  
  154.         }  
  155.           
  156.         // Generate compressed image file  
  157.         FileOutputStream fos = new FileOutputStream(outPath);    
  158.         fos.write(os.toByteArray());    
  159.         fos.flush();    
  160.         fos.close();    
  161.     }  
  162.       
  163.     /** 
  164.      * Compress by quality,  and generate image to the path specified 
  165.      *  
  166.      * @param imgPath 
  167.      * @param outPath 
  168.      * @param maxSize target will be compressed to be smaller than this size.(kb) 
  169.      * @param needsDelete Whether delete original file after compress 
  170.      * @throws IOException  
  171.      */  
  172.     public void compressAndGenImage(String imgPath, String outPath, int maxSize, boolean needsDelete) throws IOException {  
  173.         compressAndGenImage(getBitmap(imgPath), outPath, maxSize);  
  174.           
  175.         // Delete original file  
  176.         if (needsDelete) {  
  177.             File file = new File (imgPath);  
  178.             if (file.exists()) {  
  179.                 file.delete();  
  180.             }  
  181.         }  
  182.     }  
  183.       
  184.     /** 
  185.      * Ratio and generate thumb to the path specified 
  186.      *  
  187.      * @param image 
  188.      * @param outPath 
  189.      * @param pixelW target pixel of width 
  190.      * @param pixelH target pixel of height 
  191.      * @throws FileNotFoundException 
  192.      */  
  193.     public void ratioAndGenThumb(Bitmap image, String outPath, float pixelW, float pixelH) throws FileNotFoundException {  
  194.         Bitmap bitmap = ratio(image, pixelW, pixelH);  
  195.         storeImage( bitmap, outPath);  
  196.     }  
  197.       
  198.     /** 
  199.      * Ratio and generate thumb to the path specified 
  200.      *  
  201.      * @param image 
  202.      * @param outPath 
  203.      * @param pixelW target pixel of width 
  204.      * @param pixelH target pixel of height 
  205.      * @param needsDelete Whether delete original file after compress 
  206.      * @throws FileNotFoundException 
  207.      */  
  208.     public void ratioAndGenThumb(String imgPath, String outPath, float pixelW, float pixelH, boolean needsDelete) throws FileNotFoundException {  
  209.         Bitmap bitmap = ratio(imgPath, pixelW, pixelH);  
  210.         storeImage( bitmap, outPath);  
  211.           
  212.         // Delete original file  
  213.                 if (needsDelete) {  
  214.                     File file = new File (imgPath);  
  215.                     if (file.exists()) {  
  216.                         file.delete();  
  217.                     }  
  218.                 }  
  219.     }  
  220.       
  221. }  


如果上面的工具類不滿足你,那麼看看下面的方法。

一、圖片質量壓縮
[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * 質量壓縮方法 
  3.  * 
  4.  * @param image 
  5.  * @return 
  6.  */  
  7. public static Bitmap compressImage(Bitmap image) {  
  8.   
  9.     ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  10.     image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 質量壓縮方法,這裏100表示不壓縮,把壓縮後的數據存放到baos中  
  11.     int options = 90;  
  12.   
  13.     while (baos.toByteArray().length / 1024 > 100) { // 循環判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮  
  14.         baos.reset(); // 重置baos即清空baos  
  15.         image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 這裏壓縮options%,把壓縮後的數據存放到baos中  
  16.         options -= 10;// 每次都減少10  
  17.     }  
  18.     ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把壓縮後的數據baos存放到ByteArrayInputStream中  
  19.     Bitmap bitmap = BitmapFactory.decodeStream(isBm, nullnull);// 把ByteArrayInputStream數據生成圖片  
  20.     return bitmap;  
  21. }  

二、按比例大小壓縮 (路徑獲取圖片)

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * 圖片按比例大小壓縮方法 
  3.  * 
  4.  * @param srcPath (根據路徑獲取圖片並壓縮) 
  5.  * @return 
  6.  */  
  7. public static Bitmap getimage(String srcPath) {  
  8.   
  9.     BitmapFactory.Options newOpts = new BitmapFactory.Options();  
  10.     // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了  
  11.     newOpts.inJustDecodeBounds = true;  
  12.     Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此時返回bm爲空  
  13.   
  14.     newOpts.inJustDecodeBounds = false;  
  15.     int w = newOpts.outWidth;  
  16.     int h = newOpts.outHeight;  
  17.     // 現在主流手機比較多是800*480分辨率,所以高和寬我們設置爲  
  18.     float hh = 800f;// 這裏設置高度爲800f  
  19.     float ww = 480f;// 這裏設置寬度爲480f  
  20.     // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可  
  21.     int be = 1;// be=1表示不縮放  
  22.     if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放  
  23.         be = (int) (newOpts.outWidth / ww);  
  24.     } else if (w < h && h > hh) {// 如果高度高的話根據寬度固定大小縮放  
  25.         be = (int) (newOpts.outHeight / hh);  
  26.     }  
  27.     if (be <= 0)  
  28.         be = 1;  
  29.     newOpts.inSampleSize = be;// 設置縮放比例  
  30.     // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了  
  31.     bitmap = BitmapFactory.decodeFile(srcPath, newOpts);  
  32.     return compressImage(bitmap);// 壓縮好比例大小後再進行質量壓縮  
  33. }  
三、按比例大小壓縮 (Bitmap)

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * 圖片按比例大小壓縮方法 
  3.  * 
  4.  * @param image (根據Bitmap圖片壓縮) 
  5.  * @return 
  6.  */  
  7. public static Bitmap compressScale(Bitmap image) {  
  8.   
  9.     ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  10.     image.compress(Bitmap.CompressFormat.JPEG, 100, baos);  
  11.   
  12.     // 判斷如果圖片大於1M,進行壓縮避免在生成圖片(BitmapFactory.decodeStream)時溢出  
  13.     if (baos.toByteArray().length / 1024 > 1024) {  
  14.         baos.reset();// 重置baos即清空baos  
  15.         image.compress(Bitmap.CompressFormat.JPEG, 80, baos);// 這裏壓縮50%,把壓縮後的數據存放到baos中  
  16.     }  
  17.     ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());  
  18.     BitmapFactory.Options newOpts = new BitmapFactory.Options();  
  19.     // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了  
  20.     newOpts.inJustDecodeBounds = true;  
  21.     Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);  
  22.     newOpts.inJustDecodeBounds = false;  
  23.     int w = newOpts.outWidth;  
  24.     int h = newOpts.outHeight;  
  25.     Log.i(TAG, w + "---------------" + h);  
  26.     // 現在主流手機比較多是800*480分辨率,所以高和寬我們設置爲  
  27.     // float hh = 800f;// 這裏設置高度爲800f  
  28.     // float ww = 480f;// 這裏設置寬度爲480f  
  29.     float hh = 512f;  
  30.     float ww = 512f;  
  31.     // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可  
  32.     int be = 1;// be=1表示不縮放  
  33.     if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放  
  34.         be = (int) (newOpts.outWidth / ww);  
  35.     } else if (w < h && h > hh) { // 如果高度高的話根據高度固定大小縮放  
  36.         be = (int) (newOpts.outHeight / hh);  
  37.     }  
  38.     if (be <= 0)  
  39.         be = 1;  
  40.     newOpts.inSampleSize = be; // 設置縮放比例  
  41.     // newOpts.inPreferredConfig = Config.RGB_565;//降低圖片從ARGB888到RGB565  
  42.   
  43.     // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了  
  44.     isBm = new ByteArrayInputStream(baos.toByteArray());  
  45.     bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);  
  46.   
  47.     return compressImage(bitmap);// 壓縮好比例大小後再進行質量壓縮  
  48.   
  49.     //return bitmap;  
  50. }  





--------------------------------------------------------------------------------------------------------------------------------

分享個按照圖片尺寸壓縮:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. public static void compressPicture(String srcPath, String desPath) {  
  2.         FileOutputStream fos = null;  
  3.         BitmapFactory.Options op = new BitmapFactory.Options();  
  4.   
  5.         // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了  
  6.         op.inJustDecodeBounds = true;  
  7.         Bitmap bitmap = BitmapFactory.decodeFile(srcPath, op);  
  8.         op.inJustDecodeBounds = false;  
  9.   
  10.         // 縮放圖片的尺寸  
  11.         float w = op.outWidth;  
  12.         float h = op.outHeight;  
  13.         float hh = 1024f;//  
  14.         float ww = 1024f;//  
  15.         // 最長寬度或高度1024  
  16.         float be = 1.0f;  
  17.         if (w > h && w > ww) {  
  18.             be = (float) (w / ww);  
  19.         } else if (w < h && h > hh) {  
  20.             be = (float) (h / hh);  
  21.         }  
  22.         if (be <= 0) {  
  23.             be = 1.0f;  
  24.         }  
  25.         op.inSampleSize = (int) be;// 設置縮放比例,這個數字越大,圖片大小越小.  
  26.         // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了  
  27.         bitmap = BitmapFactory.decodeFile(srcPath, op);  
  28.         int desWidth = (int) (w / be);  
  29.         int desHeight = (int) (h / be);  
  30.         bitmap = Bitmap.createScaledBitmap(bitmap, desWidth, desHeight, true);  
  31.         try {  
  32.             fos = new FileOutputStream(desPath);  
  33.             if (bitmap != null) {  
  34.                 bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);  
  35.             }  
  36.         } catch (FileNotFoundException e) {  
  37.             e.printStackTrace();  
  38.         }  
  39.     }  


需要注意兩個問題:

一、調用getDrawingCache()前先要測量,否則的話得到的bitmap爲null,這個我在OnCreate()、OnStart()、OnResume()方法裏都試驗過。


二、當調用bitmap.compress(CompressFormat.JPEG, 100, fos);保存爲圖片時發現圖片背景爲黑色,如下圖:


這時只需要改成用png保存就可以了,bitmap.compress(CompressFormat.PNG, 100, fos);,如下圖:



在實際開發中,有時候我們需求將文件轉換爲字符串,然後作爲參數進行上傳。

必備工具類圖片bitmap轉成字符串string與String字符串轉換爲bitmap圖片格式

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. import android.graphics.Bitmap;  
  2. import android.graphics.BitmapFactory;  
  3. import android.util.Base64;  
  4.   
  5. import java.io.ByteArrayOutputStream;  
  6.   
  7. /** 
  8.  *  
  9.  *  
  10.  * 功能描述:Android開發之常用必備工具類圖片bitmap轉成字符串string與String字符串轉換爲bitmap圖片格式 
  11.  */  
  12. public class BitmapAndStringUtils {  
  13.     /** 
  14.      * 圖片轉成string 
  15.      * 
  16.      * @param bitmap 
  17.      * @return 
  18.      */  
  19.     public static String convertIconToString(Bitmap bitmap)  
  20.     {  
  21.         ByteArrayOutputStream baos = new ByteArrayOutputStream();// outputstream  
  22.         bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);  
  23.         byte[] appicon = baos.toByteArray();// 轉爲byte數組  
  24.         return Base64.encodeToString(appicon, Base64.DEFAULT);  
  25.   
  26.     }  
  27.   
  28.     /** 
  29.      * string轉成bitmap 
  30.      * 
  31.      * @param st 
  32.      */  
  33.     public static Bitmap convertStringToIcon(String st)  
  34.     {  
  35.         // OutputStream out;  
  36.         Bitmap bitmap = null;  
  37.         try  
  38.         {  
  39.             // out = new FileOutputStream("/sdcard/aa.jpg");  
  40.             byte[] bitmapArray;  
  41.             bitmapArray = Base64.decode(st, Base64.DEFAULT);  
  42.             bitmap =  
  43.                     BitmapFactory.decodeByteArray(bitmapArray, 0,  
  44.                             bitmapArray.length);  
  45.             // bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);  
  46.             return bitmap;  
  47.         }  
  48.         catch (Exception e)  
  49.         {  
  50.             return null;  
  51.         }  
  52.     }  
  53. }  

如果你的圖片是File文件,可以用下面代碼:

[java] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. /** 
  2.  * 圖片文件轉換爲指定編碼的字符串 
  3.  * 
  4.  * @param imgFile  圖片文件 
  5.  */  
  6. public static String file2String(File imgFile) {  
  7.     InputStream in = null;  
  8.     byte[] data = null;  
  9.     //讀取圖片字節數組  
  10.     try{  
  11.         in = new FileInputStream(imgFile);  
  12.         data = new byte[in.available()];  
  13.         in.read(data);  
  14.         in.close();  
  15.     } catch (IOException e){  
  16.         e.printStackTrace();  
  17.     }  
  18.     //對字節數組Base64編碼  
  19.     BASE64Encoder encoder = new BASE64Encoder();  
  20.     String result = encoder.encode(data);  
  21.     return result;//返回Base64編碼過的字節數組字符串  
  22. }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章