最近项目需求中,有对图片上传需求,既不能压缩过小导致不清晰,又不能原图上传,导致上传过慢,不仅浪费了流量,体验上也不好
影响图片大小的一个是图片质量,一个是图片像素。压缩方法无非是,采样率压缩和质量压缩,还有一个是微信压缩(这个请自行学习,涉及到jni层面)
图片像素:图片像素大家应该都知道,不知道的请自行百度。为了解决大图片在控件上引起崩溃,我们通常的做法就是修改采样率,采样率是什么?可以简单理解,它与像素有关。修改采样率即修改了像素
图片质量:这个可以理解成图片的饱和度,感光率等等吧!它很大程度上影响了图片的大小,但是不能够无限压缩,因为就算质量压缩没了,也不会影响像素,这是两个概念。
那么在app上传图片的时候,如何保证图片清晰且压缩的较小?
经过多轮测试,最后得出的结果就是,用长宽比率进行采样率的等比压缩,然后进行质量压缩,循环判断是否小于100k,如果不是,那么继续压缩,一直到小于100k
附上代码
这是采样率的等比压缩
/**
* 修改图片的采样率
* @param reqWidth
* @param reqHeight
* @return
*/
public static Bitmap decodeSampledBitmapFromData(String imgPath,int reqWidth, int reqHeight) {
try {
// 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgPath, options);
int minSize = options.outWidth > options.outHeight ? options.outHeight : options.outWidth;
if (minSize > reqWidth) {
// 调用上面定义的方法计算inSampleSize值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
}
// 使用获取到的inSampleSize值再次解析图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(imgPath, options);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 计算图片缩放的比例
* @param options
* @param reqWidth
* @param reqHeight
* @return
*/
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// 源图片的高度和宽度
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
// 计算出实际宽高和目标宽高的比率
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
这是图片的质量压缩(借用下别人的代码)- private Bitmap compressImage(Bitmap image) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
- int options = 100;
- while ( baos.toByteArray().length / 1024>100) { //循环判断如果压缩后图片是否大于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;
- }
附一个压缩后本地保存方法
/**
* 将压缩后的图片写入sd卡
*
*/
public static String inputPicTosdcard(Bitmap bitmap, String picName) {
String path = "";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 80;
while ( baos.toByteArray().length / 1024>200) { //循环判断如果压缩后图片是否大于100kb,大于继续压缩
baos.reset();//重置baos即清空baos
bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中
if(options>=10){
options -= 10;//每次都减少10
}else {
break;
}
}
File SDFile = Environment.getExternalStorageDirectory();
File destDir = new File(SDFile.getAbsolutePath() + "/文件夹名称/");//文件目录
if (!destDir.exists()) {//判断目录是否存在,不存在创建
destDir.mkdir();//创建目录
}
File pic = new File(destDir.getPath() + File.separator +picName);
path = pic.getAbsolutePath();
if (!pic.exists()) {
try {
pic.createNewFile();//创建文件
FileOutputStream outputStream = new FileOutputStream(pic, true);
outputStream.write(baos.toByteArray());//写入内容
outputStream.close();//关闭流
} catch (IOException e) {
e.printStackTrace();
}
}
}
return path;
}
注意:如果光进行质量压缩,压缩到一定程度就压缩不下去了啊!光采样率压缩,可能会造成失真啊。
大概能看懂就行,然后根据自己的需求去改。
http://104zz.iteye.com/blog/1694762
http://blog.youkuaiyun.com/eiuly/article/details/46648215