天天看點

android圖檔壓縮辦法總結

在Android中 如果 有圖檔 就 狠狠地壓縮處理,如果 不處理,到最後處理起來 很麻煩的,引起不必要的麻煩,和浪費時間……

這裡 提供一個工具類:

package com.jiayinlending.function.mine;

/**
 * Created by wenjing on 2017/5/26.
 */

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.util.Base64;
import android.util.Log;

/**
 * Image compress factory class
 *
 * @author
 *
 */
public class ImageFactory {

    /**
     * Get bitmap from specified image path
     *
     * @param imgPath
     * @return
     */
    public Bitmap getBitmap(String imgPath) {
        // Get bitmap through image path
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        newOpts.inJustDecodeBounds = false;
        newOpts.inPurgeable = true;
        newOpts.inInputShareable = true;
        // Do not compress
        newOpts.inSampleSize = 1;
        newOpts.inPreferredConfig = Config.RGB_565;
        return BitmapFactory.decodeFile(imgPath, newOpts);
    }

    /**
     * Store bitmap into specified image path
     *
     * @param bitmap
     * @param outPath
     * @throws FileNotFoundException
     */
    public void storeImage(Bitmap bitmap, String outPath) throws FileNotFoundException {
        FileOutputStream os = new FileOutputStream(outPath);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
    }

    /**
     * Compress image by pixel, this will modify image width/height.
     * Used to get thumbnail
     *
     * @param imgPath image path
     * @param pixelW target pixel of width
     * @param pixelH target pixel of height
     * @return
     */
    public Bitmap ratio(String imgPath, float pixelW, float pixelH) {
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 開始讀入圖檔,此時把options.inJustDecodeBounds 設回true,即隻讀邊不讀内容
        newOpts.inJustDecodeBounds = true;
        newOpts.inPreferredConfig = Config.RGB_565;
        // Get bitmap info, but notice that bitmap is null now
        Bitmap bitmap = BitmapFactory.decodeFile(imgPath,newOpts);

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 想要縮放的目标尺寸
        float hh = pixelH;// 設定高度為240f時,可以明顯看到圖檔縮小了
        float ww = pixelW;// 設定寬度為120f,可以明顯看到圖檔縮小了
        // 縮放比。由于是固定比例縮放,隻用高或者寬其中一個資料進行計算即可
        int be = 1;//be=1表示不縮放
        if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0) be = 1;
        newOpts.inSampleSize = be;//設定縮放比例
        // 開始壓縮圖檔,注意此時已經把options.inJustDecodeBounds 設回false了
        bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
        // 壓縮好比例大小後再進行品質壓縮
//        return compress(bitmap, maxSize); // 這裡再進行品質壓縮的意義不大,反而耗資源,删除
        return bitmap;
    }

    /**
     * Compress image by size, this will modify image width/height.
     * Used to get thumbnail
     *
     * @param image
     * @param pixelW target pixel of width
     * @param pixelH target pixel of height
     * @return
     */
    public Bitmap ratio(Bitmap image, float pixelW, float pixelH) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, os);
        if( os.toByteArray().length / 1024>1024) {//判斷如果圖檔大于1M,進行壓縮避免在生成圖檔(BitmapFactory.decodeStream)時溢出
            os.reset();//重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, 50, os);//這裡壓縮50%,把壓縮後的資料存放到baos中
        }
        ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        //開始讀入圖檔,此時把options.inJustDecodeBounds 設回true了
        newOpts.inJustDecodeBounds = true;
        newOpts.inPreferredConfig = Config.RGB_565;
        Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        float hh = pixelH;// 設定高度為240f時,可以明顯看到圖檔縮小了
        float ww = pixelW;// 設定寬度為120f,可以明顯看到圖檔縮小了
        //縮放比。由于是固定比例縮放,隻用高或者寬其中一個資料進行計算即可
        int be = 1;//be=1表示不縮放
        if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0) be = 1;
        newOpts.inSampleSize = be;//設定縮放比例
        //重新讀入圖檔,注意此時已經把options.inJustDecodeBounds 設回false了
        is = new ByteArrayInputStream(os.toByteArray());
        bitmap = BitmapFactory.decodeStream(is, null, newOpts);
        //壓縮好比例大小後再進行品質壓縮
//      return compress(bitmap, maxSize); // 這裡再進行品質壓縮的意義不大,反而耗資源,删除
        return bitmap;
    }

    /**
     * Compress by quality,  and generate image to the path specified
     *
     * @param image
     * @param outPath
     * @param maxSize target will be compressed to be smaller than this size.(kb)
     * @throws IOException
     */
    public void compressAndGenImage(Bitmap image, String outPath, int maxSize) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        // scale
        int options = 100;
        // Store the bitmap into output stream(no compress)
        image.compress(Bitmap.CompressFormat.JPEG, options, os);
        // Compress by loop
        while ( os.toByteArray().length / 1024 > maxSize) {
            // Clean up os
            os.reset();
            // interval 10
            options -= 10;
            image.compress(Bitmap.CompressFormat.JPEG, options, os);
        }

        // Generate compressed image file
        FileOutputStream fos = new FileOutputStream(outPath);
        fos.write(os.toByteArray());
        fos.flush();
        fos.close();
    }

    /**
     * Compress by quality,  and generate image to the path specified
     *
     * @param imgPath
     * @param outPath
     * @param maxSize target will be compressed to be smaller than this size.(kb)
     * @param needsDelete Whether delete original file after compress
     * @throws IOException
     */
    public void compressAndGenImage(String imgPath, String outPath, int maxSize, boolean needsDelete) throws IOException {
        compressAndGenImage(getBitmap(imgPath), outPath, maxSize);

        // Delete original file
        if (needsDelete) {
            File file = new File (imgPath);
            if (file.exists()) {
                file.delete();
            }
        }
    }

    /**
     * Ratio and generate thumb to the path specified
     *
     * @param image
     * @param outPath
     * @param pixelW target pixel of width
     * @param pixelH target pixel of height
     * @throws FileNotFoundException
     */
    public void ratioAndGenThumb(Bitmap image, String outPath, float pixelW, float pixelH) throws FileNotFoundException {
        Bitmap bitmap = ratio(image, pixelW, pixelH);
        storeImage( bitmap, outPath);
    }

    /**
     * Ratio and generate thumb to the path specified
     *

     * @param outPath
     * @param pixelW target pixel of width
     * @param pixelH target pixel of height
     * @param needsDelete Whether delete original file after compress
     * @throws FileNotFoundException
     */
    public void ratioAndGenThumb(String imgPath, String outPath, float pixelW, float pixelH, boolean needsDelete) throws FileNotFoundException {
        Bitmap bitmap = ratio(imgPath, pixelW, pixelH);
        storeImage( bitmap, outPath);

        // Delete original file
        if (needsDelete) {
            File file = new File (imgPath);
            if (file.exists()) {
                file.delete();
            }
        }
    }
//    如果上面的工具類不滿足你,那麼看看下面的方法。

//    一、圖檔品質壓縮

    /**
     * 品質壓縮方法
     *
     * @param image
     * @return
     */
    public static Bitmap compressImage(Bitmap image) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 品質壓縮方法,這裡100表示不壓縮,把壓縮後的資料存放到baos中
        int options = 90;

        while (baos.toByteArray().length / 1024 > 100) { // 循環判斷如果壓縮後圖檔是否大于100kb,大于繼續壓縮
            baos.reset(); // 重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 這裡壓縮options%,把壓縮後的資料存放到baos中
            options -= 10;// 每次都減少10
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把壓縮後的資料baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream資料生成圖檔
        return bitmap;
    }

//    二、按比例大小壓縮 (路徑擷取圖檔)

    /**
     * 圖檔按比例大小壓縮方法
     *
     * @param srcPath (根據路徑擷取圖檔并壓縮)
     * @return
     */
    public static Bitmap getimage(String srcPath) {

        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 開始讀入圖檔,此時把options.inJustDecodeBounds 設回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此時傳回bm為空

        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 現在主流手機比較多是800*480分辨率,是以高和寬我們設定為
        float hh = 800f;// 這裡設定高度為800f
        float ww = 480f;// 這裡設定寬度為480f
        // 縮放比。由于是固定比例縮放,隻用高或者寬其中一個資料進行計算即可
        int be = 1;// be=1表示不縮放
        if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) {// 如果高度高的話根據寬度固定大小縮放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be;// 設定縮放比例
        // 重新讀入圖檔,注意此時已經把options.inJustDecodeBounds 設回false了
        bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
        return compressImage(bitmap);// 壓縮好比例大小後再進行品質壓縮
    }
//    三、按比例大小壓縮 (Bitmap)

    /**
     * 圖檔按比例大小壓縮方法
     *
     * @param image (根據Bitmap圖檔壓縮)
     * @return
     */
    public static Bitmap compressScale(Bitmap image) {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);

        // 判斷如果圖檔大于1M,進行壓縮避免在生成圖檔(BitmapFactory.decodeStream)時溢出
        if (baos.toByteArray().length / 1024 > 1024) {
            baos.reset();// 重置baos即清空baos
            image.compress(Bitmap.CompressFormat.JPEG, 80, baos);// 這裡壓縮50%,把壓縮後的資料存放到baos中
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
        BitmapFactory.Options newOpts = new BitmapFactory.Options();
        // 開始讀入圖檔,此時把options.inJustDecodeBounds 設回true了
        newOpts.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
        newOpts.inJustDecodeBounds = false;
        int w = newOpts.outWidth;
        int h = newOpts.outHeight;
        // 現在主流手機比較多是800*480分辨率,是以高和寬我們設定為
        // float hh = 800f;// 這裡設定高度為800f
        // float ww = 480f;// 這裡設定寬度為480f
        float hh = 512f;
        float ww = 512f;
        // 縮放比。由于是固定比例縮放,隻用高或者寬其中一個資料進行計算即可
        int be = 1;// be=1表示不縮放
        if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放
            be = (int) (newOpts.outWidth / ww);
        } else if (w < h && h > hh) { // 如果高度高的話根據高度固定大小縮放
            be = (int) (newOpts.outHeight / hh);
        }
        if (be <= 0)
            be = 1;
        newOpts.inSampleSize = be; // 設定縮放比例
        // newOpts.inPreferredConfig = Config.RGB_565;//降低圖檔從ARGB888到RGB565

        // 重新讀入圖檔,注意此時已經把options.inJustDecodeBounds 設回false了
        isBm = new ByteArrayInputStream(baos.toByteArray());
        bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);

        return compressImage(bitmap);// 壓縮好比例大小後再進行品質壓縮

        //return bitmap;
    }






//    分享個按照圖檔尺寸壓縮:

    public static void compressPicture(String srcPath, String desPath) {
        FileOutputStream fos = null;
        BitmapFactory.Options op = new BitmapFactory.Options();

        // 開始讀入圖檔,此時把options.inJustDecodeBounds 設回true了
        op.inJustDecodeBounds = true;
        Bitmap bitmap = BitmapFactory.decodeFile(srcPath, op);
        op.inJustDecodeBounds = false;

        // 縮放圖檔的尺寸
        float w = op.outWidth;
        float h = op.outHeight;
        float hh = 1024f;//
        float ww = 1024f;//
        // 最長寬度或高度1024
        float be = 1.0f;
        if (w > h && w > ww) {
            be = (float) (w / ww);
        } else if (w < h && h > hh) {
            be = (float) (h / hh);
        }
        if (be <= 0) {
            be = 1.0f;
        }
        op.inSampleSize = (int) be;// 設定縮放比例,這個數字越大,圖檔大小越小.
        // 重新讀入圖檔,注意此時已經把options.inJustDecodeBounds 設回false了
        bitmap = BitmapFactory.decodeFile(srcPath, op);
        int desWidth = (int) (w / be);
        int desHeight = (int) (h / be);
        bitmap = Bitmap.createScaledBitmap(bitmap, desWidth, desHeight, true);
        try {
            fos = new FileOutputStream(desPath);
            if (bitmap != null) {
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }


 /*   需要注意兩個問題:
    一、調用getDrawingCache()前先要測量,否則的話得到的bitmap為null,這個我在OnCreate()、OnStart()、OnResume()方法裡都試驗過。

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

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



    在實際開發中,有時候我們需求将檔案轉換為字元串,然後作為參數進行上傳。
    必備工具類圖檔bitmap轉成字元串string與String字元串轉換為bitmap圖檔格式*/
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.util.Base64;

    import java.io.ByteArrayOutputStream;

    /**
     *
     *
     * 功能描述:Android開發之常用必備工具類圖檔bitmap轉成字元串string與String字元串轉換為bitmap圖檔格式
     */
    public class BitmapAndStringUtils {
        /**
         * 圖檔轉成string
         *
         * @param bitmap
         * @return
         */
        public static String convertIconToString(Bitmap bitmap)
        {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();// outputstream
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
            byte[] appicon = baos.toByteArray();// 轉為byte數組
            return Base64.encodeToString(appicon, Base64.DEFAULT);

        }

        /**
         * string轉成bitmap
         *
         * @param st
         */
        public static Bitmap convertStringToIcon(String st)
        {
            // OutputStream out;
            Bitmap bitmap = null;
            try
            {
                // out = new FileOutputStream("/sdcard/aa.jpg");
                byte[] bitmapArray;
                bitmapArray = Base64.decode(st, Base64.DEFAULT);
                bitmap =
                        BitmapFactory.decodeByteArray(bitmapArray, 0,
                                bitmapArray.length);
                // bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
                return bitmap;
            }
            catch (Exception e)
            {
                return null;
            }
        }
    }

//    如果你的圖檔是File檔案,可以用下面代碼:

    /**
     * 圖檔檔案轉換為指定編碼的字元串
     *
     * @param imgFile  圖檔檔案
     */
    public static String file2String(File imgFile) {
        InputStream in = null;
        byte[] data = null;
        //讀取圖檔位元組數組
        try{
            in = new FileInputStream(imgFile);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException e){
            e.printStackTrace();
        }
        //對位元組數組Base64編碼
        BASE64Encoder encoder = new BASE64Encoder();
        String result = encoder.encode(data);
        return result;//傳回Base64編碼過的位元組數組字元串
    }

}