- 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; }
}