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一下,最后返回处理后的照片路径,由网络框架进行上传,请注意的是,由于压缩图片有点耗时,所以要放在子线程中操作

 

你可能感兴趣的:(Android开发)