最近項目需求中,有對圖片上傳需求,既不能壓縮過小導致不清晰,又不能原圖上傳,導致上傳過慢,不僅浪費了流量,體驗上也不好
影響圖片大小的一個是圖片質量,一個是圖片像素。壓縮方法無非是,採樣率壓縮和質量壓縮,還有一個是微信壓縮(這個請自行學習,涉及到jni層面)
圖片像素:圖片像素大家應該都知道,不知道的請自行百度。爲了解決大圖片在控件上引起崩潰,我們通常的做法就是修改採樣率,採樣率是什麼?可以簡單理解,它與像素有關。修改採樣率即修改了像素
圖片質量:這個可以理解成圖片的飽和度,感光率等等吧!它很大程度上影響了圖片的大小,但是不能夠無限壓縮,因爲就算質量壓縮沒了,也不會影響像素,這是兩個概念。
那麼在app上傳圖片的時候,如何保證圖片清晰且壓縮的較小?
經過多輪測試,最後得出的結果就是,用長寬比率進行採樣率的等比壓縮,然後進行質量壓縮,循環判斷是否小於100k,如果不是,那麼繼續壓縮,一直到小於100k
附上代碼
這是採樣率的等比壓縮
/**
* 修改圖片的採樣率
* @param reqWidth
* @param reqHeight
* @return
*/
public static Bitmap decodeSampledBitmapFromData(String imgPath,int reqWidth, int reqHeight) {
try {
// 第一次解析將inJustDecodeBounds設置爲true,來獲取圖片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgPath, options);
int minSize = options.outWidth > options.outHeight ? options.outHeight : options.outWidth;
if (minSize > reqWidth) {
// 調用上面定義的方法計算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
}
// 使用獲取到的inSampleSize值再次解析圖片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imgPath, options);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 計算圖片縮放的比例
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源圖片的高度和寬度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 計算出實際寬高和目標寬高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 選擇寬和高中最小的比率作爲inSampleSize的值,這樣可以保證最終圖片的寬和高
// 一定都會大於等於目標的寬和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
這是圖片的質量壓縮(借用下別人的代碼)- private Bitmap compressImage(Bitmap image) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質量壓縮方法,這裏100表示不壓縮,把壓縮後的數據存放到baos中
- int options = 100;
- 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;
- }
附一個壓縮後本地保存方法
/**
* 將壓縮後的圖片寫入sd卡
*
*/
public static String inputPicTosdcard(Bitmap bitmap, String picName) {
String path = "";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質量壓縮方法,這裏100表示不壓縮,把壓縮後的數據存放到baos中
int options = 80;
while ( baos.toByteArray().length / 1024>200) { //循環判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮
baos.reset();//重置baos即清空baos
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//這裏壓縮options%,把壓縮後的數據存放到baos中
if(options>=10){
options -= 10;//每次都減少10
}else {
break;
}
}
File SDFile = Environment.getExternalStorageDirectory();
File destDir = new File(SDFile.getAbsolutePath() + "/文件夾名稱/");//文件目錄
if (!destDir.exists()) {//判斷目錄是否存在,不存在創建
destDir.mkdir();//創建目錄
}
File pic = new File(destDir.getPath() + File.separator +picName);
path = pic.getAbsolutePath();
if (!pic.exists()) {
try {
pic.createNewFile();//創建文件
FileOutputStream outputStream = new FileOutputStream(pic, true);
outputStream.write(baos.toByteArray());//寫入內容
outputStream.close();//關閉流
} catch (IOException e) {
e.printStackTrace();
}
}
}
return path;
}
注意:如果光進行質量壓縮,壓縮到一定程度就壓縮不下去了啊!光採樣率壓縮,可能會造成失真啊。
大概能看懂就行,然後根據自己的需求去改。
http://104zz.iteye.com/blog/1694762
http://blog.csdn.net/eiuly/article/details/46648215