- CompressUtil 流程图:
CompressUtil 类 具体解释
public class CompressUtil {
/**
* 终于封装的压缩方法
* @param imgPath
* @return
*/
public static Bitmap process(String imgPath){
int degree = readPictureDegree(imgPath); //获取旋转角度
Bitmap bmp =getBmpByMaxSize(imgPath, 480); //获取当前路径图片的位图,最大尺寸180*1024
if (degree != 0) {
bmp = rotaingImageView(degree, bmp); //有旋转角度的旋转角度
}
return bmp;
}
/**
* 读取图片属性:旋转的角度
* @param path 图片绝对路径
* @return degree旋转的角度
*/
@SuppressLint("NewApi")
public static int readPictureDegree(String path){
int degree = 0; //设置默认图片角度为0度
try {
/**
* Exif: 图像信息
* Exif是一种图像文件格式。它的数据存储与JPEG格式是全然同样的。
* 实际上Exif格式就是在JPEG格式头部插入了数码照片的信息。包含
* 拍摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄
* 条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及GPS全
* 球定位系统数据、缩略图等。
你能够利用不论什么能够查看JPEG文件的
* 看图软件浏览Exif格式的照片,但并非全部的图形程序都能处理
* Exif信息。
*
* ExifInterface: 图像信息接口类
*/
/**
* ExifInterface构造函数 ExifInterface(String filename)
* 从指定的JPEG文件里读取EXIF标签。
*/
//获取指定图片的图片处理对象
ExifInterface exifInterface = new ExifInterface(path);
/**
* int <- getAttributeInt(String tag, int defaultValue)
* 返回指定标记的整数值
*
* Attribute : 属性
*
* tag : 指标 (一共21个)
* ExifInterface.TAG_APERTURE => 光圈指标
* ExifInterface.TAG_DATETIME => 日期时间指标
* ExifInterface.TAG_EXPOSURE_TIME => 公布时间指标
* ExifInterface.TAG_FLASH => 闪光灯
* ExifInterface.TAG_FOCAL_LENGTH => 焦距指标
* ExifInterface.TAG_GPS_ALTITUDE => GPS海拔高度指标
* ExifInterface.TAG_GPS_ALTITUDE_REF => GPS海拔參考点指标
* ExifInterface.TAG_GPS_DATESTAMP => GPS日期戳指标
* ExifInterface.TAG_GPS_LATITUDE => GPS纬度指标
* ExifInterface.TAG_GPS_LATITUDE_REF => GPS纬度參考点指标
* ExifInterface.TAG_GPS_LONGITUDE => GPS经度指标
* ExifInterface.TAG_GPS_LONGITUDE_REF => GPS经度參考点指标
* ExifInterface.TAG_GPS_PROCESSING_METHOD => GPS加工指标
* ExifInterface.TAG_GPS_TIMESTAMP => GPS时间戳指标
* ExifInterface.TAG_IMAGE_LENGTH => 图像长度指标
* ExifInterface.TAG_IMAGE_WIDTH => 图像宽度指标
* ExifInterface.TAG_ISO => 标签
* ExifInterface.TAG_MAKE => 制作
* ExifInterface.TAG_MODEL => 模型
* ExifInterface.TAG_ORIENTATION => 定位指标
* ExifInterface.TAG_WHITE_BALANCE => 白平衡指标
*
* defaultValue : 默认值 (一共11个)
*
* ExifInterface.ORIENTATION_FLIP_HORIZONTAL => 水平翻转
* ExifInterface.ORIENTATION_FLIP_VERTICAL => 垂直翻转
* ExifInterface.ORIENTATION_NORMAL => 正常
* ExifInterface.ORIENTATION_ROTATE_180 => 旋转180度
* ExifInterface.ORIENTATION_ROTATE_270 => 旋转270度
* ExifInterface.ORIENTATION_ROTATE_90 => 旋转90度
* ExifInterface.ORIENTATION_TRANSPOSE => 取向的转置
* ExifInterface.ORIENTATION_TRANSVERSE => 方位横向
* ExifInterface.ORIENTATION_UNDEFINED => 方向没有定义
* ExifInterface.WHITEBALANCE_AUTO => 白平衡自己主动
* ExifInterface.WHITEBALANCE_MANUAL => 手动白平衡
*/
// 获取该图片的方向參数
int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_90);
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 angle
* @param bitmap
* @return Bitmap
*
* Bitmap : 位图
* Bitmap是Android系统中的图像处理的最重要类之中的一个。
* 用它能够获取图像文件信息。进行图像剪切、旋转、缩
* 放等操作。并能够指定格式保存图像文件。
*
* Bitmap实如今android.graphics包中。
可是Bitmap
* 类的构造函数是私有的,外面并不能实例化。仅仅能是通
* 过JNI实例化。
这必定是 某个辅助类提供了创建Bitmap
* 的接口,而这个类的实现通过JNI接口来实例化Bitmap的,
* 这个类就是BitmapFactory。
*
* decode : 解码
*
* BitmapFactory.decodeFile(String pathName)
* BitmapFactory.decodeFile(String pathName,Options opts)
* BitmapFactory.decodeResource(Resource res,int id)
* BitmapFactory.decodeResource(Resource res,int id,Options opts)
*
* 利用BitmapFactory能够从一个指定文件里,利用decodeFile()解出Bitmap;
* 也能够定义的图片资源中,利用decodeResource()解出Bitmap
*
* 当中Options是decode时的选项
* 在用法decodeFile()/decodeResource()时。都能够指定一个BitmapFacotry.Options。
*
* 利用Options的下列属性,能够指定decode的选项
* inPreferredConfig => decode到内存中,手机中所採用的编码,可选值定义在Bitmap.Config中。缺省值是ARGB_8888
* inJustDecodeBounds => 假设设置为true,并不会把图像的数据全然解码,亦即decodeXyz()返回值为null,可是Options的outAbc中解出了图像的基本信息
* inSampleSize => 设置decode时的缩放比例
*
*/
public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {
// Bitmap能够和Matrix结合实现图像的剪切、旋转、缩放等操作
//获取Matrix对象
Matrix matrix = new Matrix();
//设置旋转角度
matrix.postRotate(angle);
// 创建新的图片
Bitmap resizedBitmap=bitmap;
/**
* 用原Bitmap通过变换生成新的Bitmap的方法:
*
* public static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height,Matrix m,boolean filter)
* 这样的方法是终于的实现,后两种仅仅是对这样的方法的封装
*
* public static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height)
* 这样的方法能够从源Bitmap中指定区域(x,y, width, height)中挖出一块来实现剪切
*
* public static Bitmap createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter)
* 这样的方法能够把源Bitmap缩放为dstWidth x dstHeight的Bitmap
*
* filter => 设为true => 对Bitmap进行滤波处理,会有抗锯齿的效果
*/
try {
resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
/**
* Bitmap的recycle问题
* recycle : 回收
* 尽管Android有自己的垃圾回收机制,对于是不是要我们自己调用recycle。还的看情况而定。
* 假设仅仅是使用少量的几张图片,回收与否关系不大。可是若有大量bitmap须要垃圾回收处理,
* 那必定垃圾回收须要做的次数就很多其它也发生地更频繁。会对系统资源造成负荷。所以,这个时
* 候还是自己试用recycle来释放的比較好。
*
* 注意 => 仅仅有当你确认你不会在使用这个bitmap的时候。就能够选择调用recycle()方法释放它。
*
*/
bitmap.recycle();
//进行垃圾回收
System.gc();
}catch (OutOfMemoryError e){
e.printStackTrace();
}
return resizedBitmap;
}
/**
* 降低图片质量压缩
* @param bmp
* @param maxSize
* @param fileSize
* @return
*/
public static Bitmap compressBmp(Bitmap bmp, int maxSize, long fileSize) {
Bitmap newBmp=bmp;
//ByteArrayOutputStream => 捕获内存缓冲区的数据,转换成字节数组。
ByteArrayOutputStream baos=null;
//ByteArrayInputStream => 将字节数组转化为输入流
ByteArrayInputStream bais=null;
int quality = 100;
if(fileSize>4*1024*1024){
quality=40;
}else if(fileSize>2*1024*1024){
quality=50;
}else if(fileSize>800*1024){
quality=60;
}
try {
/**
* ByteArrayOutputStream类是在创建它的实例时,程序内部创建一个byte型别数组的缓冲区,
* 然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写入或读出
* byte型数据。在网络传输中我们往往要传输非常多变量。我们能够利用ByteArrayOutputStream
* 把全部的变量收集到一起。然后一次性把数据发送出去。
*/
baos = new ByteArrayOutputStream();
System.out.print("開始压缩: " + quality);
/**
* 图片压缩
* Bitmap.compress(CompressFormat format, int quality, OutputStream stream)
* 方法的參数format可设置JPEG或PNG格式;quality可选择压缩质量;fOut是输出流(OutputStream)
*/
bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
float maxByte = maxSize * 1024;
baos.flush();
float scale = 1;
while (baos.size() > maxByte) {
System.out.print("压缩大小:" + baos.size() / 1024);
System.out.print("压缩大小2:" + baos.toByteArray().length / 1024);
scale = Math.round((float) baos.size() / maxByte);
if (scale < 1) scale = 1;
quality -= scale * 2;
baos.reset(); //重置流,使流计数=0。
重置该流丢弃全部当前累积输出。
bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
baos.flush();
}
// File file=new File(FileUtil.getAudioPath()+File.separator+System.currentTimeMillis()+”.jpg”);
// BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(file));
// bos.write(baos.toByteArray());
// bos.flush();
// bos.close();
System.out.print("压缩后大小:" + baos.size() / 1024);
bais = new ByteArrayInputStream(baos.toByteArray());
baos.flush();
newBmp = BitmapFactory.decodeStream(bais);
bmp.recycle();
System.gc();
}catch (OutOfMemoryError e){
e.printStackTrace();
//内存溢出则压缩很多其它
if(!bmp.isRecycled()) {
try {
quality=quality/2;
baos = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
bais = new ByteArrayInputStream(baos.toByteArray());
baos.flush();
newBmp = BitmapFactory.decodeStream(bais);
}catch (Exception ex){}
}
}catch (Exception e){
e.printStackTrace();
}finally{
try {
if (bais != null) {
bais.close();
}
if (baos != null) {
baos.close();
}
}catch (Exception e){}
}
return newBmp;
}
public static Bitmap getBmpByMaxSize(String path, int maxSize){
/**
* Android使用BitmapFactory.Options解决载入大图片内存溢出问题
*
* 因为Android对图片使用内存有限制。若是载入几兆的大图片便内存溢出。
* Bitmap会将图片的全部像素(即长x宽)载入到内存中,假设图片分辨率
* 过大,会直接导致内存溢出(java.lang.OutOfMemoryError),仅仅有
* 在BitmapFactory载入图片时使用BitmapFactory.Options对相关參
* 数进行配置来降低载入的像素。
*
* BitmapFactory.Options这个类。有一个字段叫做 inJustDecodeBounds 。
* 假设我们把它设为true。那么BitmapFactory.decodeFile(String path,
* Options opt)并不会真的返回一个Bitmap给你,它仅仅会把它的宽。高取回
* 来给你,这样就不会占用太多的内存。也就不会那么频繁的发生OOM(Out Of
* Memory)了。
*
*/
BitmapFactory.Options options=new BitmapFactory.Options();
//使图片最小边框缩小到800像素
options.inJustDecodeBounds=true;
//这里返回的bitmap=null,但能够通过options.outWidth 和 options.outHeight就是我们想要的宽和高了
BitmapFactory.decodeFile(path, options);
//最短的永远都是宽度
double width=options.outWidth<options.outHeight? options.outWidth: options.outHeight;
//实际宽度/理想宽度 => 上传图片缩放比例
int sampleSize=(int)Math.round(width/480);
System.out.print("上传图片缩放比例:" + sampleSize);
if(sampleSize<1) sampleSize=1;
/**
* inSampleSize表示缩略图大小为原始图片大小的几分之中的一个,
* 即假设这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。
*/
options.inSampleSize = sampleSize;
options.inJustDecodeBounds=false;
options.inDither=false; /*不进行图片抖动处理*/
options.inPreferredConfig=null; /*设置让解码器以最佳方式解码*/
/**
* 以下两个字段须要组合使用 节约内存
*/
options.inPurgeable = true; // 同意可清除
options.inInputShareable = true;
long length=new File(path).length();
if(length>(1.5*1024*1024)){ //大于1.5M时
options.inSampleSize+=(int)(length/1024/1024)*0.5; //当大于2M时为避免内存溢出缩小
}
Bitmap bitmap=null;
try {
bitmap = BitmapFactory.decodeFile(path, options);
}catch (OutOfMemoryError e){
e.printStackTrace();
//内存溢出则将图片缩小一半
if(options.inSampleSize<1) options.inSampleSize=1;
options.inSampleSize=options.inSampleSize*2;
bitmap=BitmapFactory.decodeFile(path, options);
}
if(length>(800*1024)) { //大于20K字节压缩
bitmap = compressBmp(bitmap, maxSize, length);
}
return bitmap;
}
}