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