Android图片压缩上传和解决上传服务器之后图片旋转的问题

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:34   1019   0

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

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP