android圖片壓縮不失真實戰

BinaryKNight01發表於2017-08-30

      相信大家在專案開發中,不管是大小企業,伺服器上傳速度都是有限的,更何況不少公司是通過存在第三方的雲端儲存如七牛中間站的,這就造成假設我們需要上傳多張圖片會很慢,給使用者很差的體驗了,這裡是個人在專案中需求需要上傳三十張照片,對圖片壓縮的心得,可能存在不足的地方,希望能指正。

廢話客套話就不多說了,我們搬磚裝修吧,核心實現程式碼如下:

// 根據路徑獲得圖片並壓縮,返回bitmap用於顯示

public static Bitmap getSmallBitmap(String filePath) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);

        // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, 480, 800);

        // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;

    return BitmapFactory.decodeFile(filePath, options);
    }
//把bitmap轉換成String
public static String bitmapToString(String filePath) {

        Bitmap bm = getSmallBitmap(filePath);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.JPEG, 40, baos);
        byte[] b = baos.toByteArray();
        return Base64.encodeToString(b, Base64.DEFAULT);
    }
另外在我們儲存圖片還可以使用Base64轉化為位元組流儲存檔案,檔案傳輸入,為了傳輸方便最好的辦法是將檔案轉化成base64字串,再將base64字串轉成位元組流儲存在檔案了。不過這種做法雖然簡單,但還要根據實際需求進行選擇,弊端是不能轉太大的檔案,檔案太大會造成效率問題,現在暫且這麼處理。
具體程式碼如下:

public void saveBitmap(String filePath) {

        try {
            String path = Environment.getExternalStorageDirectory() + "/test" + System.currentTimeMillis() + ".png";
            decoderBase64File(bitmapToString(filePath), path);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * decoderBase64File:(將base64字元解碼儲存檔案). <br/>
     *
     * @param base64Code 編碼後的字串
     * @param savePath   檔案儲存路徑
     * @throws Exception
     * @author BinaryKnight
     * @since JDK 1.6
     */
    public void decoderBase64File(String base64Code, String savePath) throws Exception {
//byte[] buffer = new BASE64Decoder().decodeBuffer(base64Code);
        byte[] buffer = Base64.decode(base64Code, Base64.DEFAULT);
        File f = new File(savePath);
        f.createNewFile();
        FileOutputStream out = new FileOutputStream(f);
        out.write(buffer);
        out.close();
    }



對圖片壓縮的講解:壓縮圖片我們需要知道圖片的尺寸,然後根據比例無失真壓縮:
主要分成以下三步:
1.獲取原始圖片的長和寬
	BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);
        int height = options.outHeight;
        int width = options.outWidth; 
以上程式碼是對圖片進行解碼,inJustDecodeBounds設定為true,可以不把圖片讀到記憶體中,但依然可以計算出圖片的大小,這正好可以滿足我們第一步的需要
2.計算壓縮比例	
  int height = options.outHeight;
     int width = options.outWidth; 
     int inSampleSize = 1;
     int reqHeight=800;
     int reqWidth=480;
     if (height > reqHeight || width > reqWidth) {
    final int heightRatio = Math.round((float) height/ (float) reqHeight);
    final int widthRatio = Math.round((float) width / (float) reqWidth);            
    inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
     }
這裡以手機解析度480*800為例,我們期望的寬高分別設定為480,800,這兩個值只是我們的期望值,實際壓縮後的寬度和高度都會比期望的要大。如果原始圖片的尺寸大於我們設定的期望值才會進行壓縮,否則不壓縮。heightRatio是圖片原始高度與壓縮後高度的倍數,widthRatio是圖片原始寬度與壓縮後寬度的倍數。inSampleSize為heightRatio與widthRatio中最小的那個,inSampleSize就是縮放值。 inSampleSize為1表示寬度和高度不縮放,為2表示壓縮後的寬度與高度為原來的1/2。
3.縮放並壓縮圖片
//在記憶體中建立bitmap物件,這個物件按照縮放大小建立的
        byte[] b = baos.toByteArray();options.inSampleSize = calculateInSampleSize(options, 480, 800);
 	options.inJustDecodeBounds = false;
        Bitmap bitmap= BitmapFactory.decodeFile(filePath, options);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bm.compress(Bitmap.CompressFormat.JPEG, 60, baos);
        byte[] b = baos.toByteArray();
前三行的程式碼其實已經得到了一個縮放的bitmap物件,如果你在應用中顯示圖片,就可以使用這個bitmap物件了。由於考慮到網路流量的問題。我們好需要犧牲圖片的質量來換取一部分空間,這裡呼叫bm.compress()方法進行壓縮,這個方法的第二個引數,如果是100,表示不壓縮,我這裡設定的是60,你也可以更加你的需要進行設定,在實驗的過程中我設定為30,圖片都不會失真。
壓縮效果:可以把1.5M左右的圖片壓縮到100K左右,並且沒有失真。			
另外附加乾貨:
/*
壓縮圖片,處理某些手機拍照角度旋轉的問題
*/
public static String compressImage(Context context,String filePath,String fileName,int q) throws FileNotFoundException {

        Bitmap bm = getSmallBitmap(filePath);

        int degree = readPictureDegree(filePath);

        if(degree!=0){//旋轉照片角度
            bm=rotateBitmap(bm,degree);
        }

        File imageDir = SDCardUtils.getImageDir(context);

        File outputFile=new File(imageDir,fileName);

        FileOutputStream out = new FileOutputStream(outputFile);

        bm.compress(Bitmap.CompressFormat.JPEG, q, out);

        return outputFile.getPath();
    }
判斷照片角度
public static int readPictureDegree(String path) {
        int degree = 0;
        try {
            ExifInterface exifInterface = new ExifInterface(path);
            int orientation = exifInterface.getAttributeInt(
                    ExifInterface.TAG_ORIENTATION,
                    ExifInterface.ORIENTATION_NORMAL);
            switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                degree = 90;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                degree = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                degree = 270;
                break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return degree;
    }
旋轉照片
public static Bitmap rotateBitmap(Bitmap bitmap,int degress) {
        if (bitmap != null) {
            Matrix m = new Matrix();
            m.postRotate(degress); 
            bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                    bitmap.getHeight(), m, true);
            return bitmap;
        }
        return bitmap;
    }

相關文章