Android圖片上傳是開發中最常見的應用場景,但是現在的手機攝像頭像素都非常高,隨便拍一張照片都在3~4M之間,分辨率也都在3000x4000左右,如此大的照片如果直接顯示或者上傳到服務器,體驗都是非常差的,所以圖片在上傳之前,一般要做兩次壓縮,一次尺寸壓縮,一次質量壓縮。話不多說,直接上代碼吧
一、圖片的壓縮一般寫一個工具類進行處理,我們起一個方法名就叫compress吧
/**
* 根據圖片路徑壓縮圖片並返回壓縮後圖片路徑
* @param path
* @return
*/
public static String compress(String path) {
Bitmap bm = getimage(path);//得到壓縮後的圖片
if(bm == null){
return null;
}
String file = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "temp" + File.separator;
File f = new File(file);
if(!f.exists()){
f.mkdirs();
}
String[] temps = path.split("/");
String temp = "";
if(!TextUtils.isEmpty(temps[temps.length-1])){
temp = temps[temps.length-1];
}
String picName = temp+(DateUtil.getTimestamp())+".png";
try {
FileOutputStream out = new FileOutputStream(file+picName);
bm.compress(Bitmap.CompressFormat.PNG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
f = new File(file+picName);
if(new File(path).length() < f.length()){
f.delete();
return "";
}
return file+picName;
}
二、這個方法就是得到一個壓縮後的圖片,然後把圖片寫到一個臨時目錄,返回一個壓縮後的圖片路徑,得到這個路徑之後,由網絡框架進行上傳。這個getimage方法就做兩件事,一個是尺寸壓縮,壓縮完了以後進行質量壓縮
/**
* 第二:圖片按比例大小壓縮方法(根據路徑獲取圖片並壓縮):
* @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;
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);//壓縮好比例大小後再進行質量壓縮
}
三、質量壓縮方法
/**
* 質量壓縮
* @param image
* @return
*/
public static Bitmap compressImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質量壓縮方法,這裏100表示不壓縮,把壓縮後的數據存放到baos中
int options = 99;//這個只能是0-100之間不包括0和100不然會報異常
while ( baos.toByteArray().length / 1024>100 && options > 0) { //循環判斷如果壓縮後圖片是否大於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;
}
一頓猛操作下來,一張幾M大小的圖片,已經變成了100kb以下了,也可以稍微調大一點,200kb/300kb問題都不大,而且照片還清晰一些。上傳服務器之後,服務器會爲我們生成縮略圖,所以上傳原圖清晰一點更好一些。如果是直接拍照上傳的圖片,可能還會遇到另外一個問題,就是本地查看正常的照片,傳到服務器發現照片旋轉了90°或者270°,這是因爲有些手機攝像頭的參數原因,拍出來的照片是自帶旋轉角度的。
還好數碼照片會將它的拍攝參數存放在exif當中,你可以認爲exif是照片的一些頭部信息,比如拍照的時間,相機的品牌,型號和色彩編碼等等,其中也包括了旋轉角度。Android可以通過ExifInterface來讀取圖片的exif信息。所以我們要讀取照片的旋轉角度,然後通過matrix把它旋轉過來
所以我們要在剛纔的compress方法再加兩個方法:
/**
* 讀取照片的旋轉角度
* @param path
* @return
*/
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;
}
/**
* 旋轉照片
* @param bitmap
* @param rotate
* @return
*/
private static Bitmap rotateBitmap(Bitmap bitmap, int rotate) {
int w = bitmap.getWidth();
int h = bitmap.getHeight();
Matrix matrix = new Matrix();
matrix.postRotate(rotate);
return Bitmap.createBitmap(bitmap, 0, 0, w, h, matrix, true);
}
所以完整的compress方法如下:
/**
* 根據圖片路徑壓縮圖片並返回壓縮後圖片路徑
* @param path
* @return
*/
public static String compress(String path) {
Bitmap bm = getimage(path);//壓縮照片
if(bm == null){
return null;
}
int degree = readPictureDegree(path);//讀取照片旋轉角度
if (degree > 0) {
bm = rotateBitmap(bm, degree); //旋轉照片
}
String file = Environment.getExternalStorageDirectory().getAbsolutePath()
+ File.separator + "temp" + File.separator;
File f = new File(file);
if(!f.exists()){
f.mkdirs();
}
String picName = System.currentTimeMillis() + ".jpg";
String resultFilePath = file + picName;
try {
FileOutputStream out = new FileOutputStream(resultFilePath);
bm.compress(Bitmap.CompressFormat.JPEG, 90, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
return resultFilePath;
}
一個完整的圖片上傳流程應該是先進行尺寸壓縮,然後在進行質量壓縮,然後看照片是否有旋轉角度,如果有,rotate一下,最後返回處理後的照片路徑,由網絡框架進行上傳,請注意的是,由於壓縮圖片有點耗時,所以要放在子線程中操作