Android圖片壓縮上傳和解決上傳服務器之後圖片旋轉的問題

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一下,最後返回處理後的照片路徑,由網絡框架進行上傳,請注意的是,由於壓縮圖片有點耗時,所以要放在子線程中操作

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章