在android应用开发中,涉及到本地图片上传的一般都会先对图片进行压缩再上传。
常见的压缩方式有两种:一种是利用BitmapFactory.Options对图片进行尺寸上的压缩,具体方法如下:
/**
* 把原图按比例压缩
*
* @param srcPath 原图的路径
* @param maxSize 指定的图片最大尺寸
* @return 压缩后的图片
*/
public Bitmap getCompressPhoto(String srcPath, int maxSize) {
BitmapFactory.Options options = new BitmapFactory.Options();
//开始读入图片,此时把options.inJustDecodeBounds 设回true了
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, options);//此时返回bm为空
options.inJustDecodeBounds = false;
int sampleSize = 1;//1代表不压缩
if (options.outHeight > 0 && options.outWidth > 0) {
while (options.outHeight / sampleSize > maxSize || options.outWidth / sampleSize > maxSize) {
sampleSize = sampleSize + 1;
}
} else {
sampleSize = sampleSize << 1;
}
options.inSampleSize = sampleSize;//设置缩放比例
try {
//重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, options);
} catch (OutOfMemoryError e) {
options.inSampleSize *= 2;
bitmap = BitmapFactory.decodeFile(srcPath, options);
}
return bitmap;
}
另外一种是按指定内存大小压缩,具体方法如下:
/**
* 质量压缩方法
*
* @param srcPath 原图的路径
* @param maxByte 需要压缩到多大内存大小(单位:kb)
* @return
*/
public static Bitmap compressImage(String srcPath, int maxByte) {
Bitmap image = BitmapFactory.decodeFile(srcPath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 90;
while (baos.toByteArray().length / 1024 > maxByte) { // 循环判断如果压缩后图片是否大于maxByte,大于继续压缩
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;
}
注意,上面两种方法得到的都是Bitmap 对象,而上传到服务器的图片一般都是传一个File类型,所以还要把压缩后的Bitmap 存储到本地,然后根据存储路径得到File来上传。那么问题就来了,第一种方法确实可以得到预期的尺寸压缩效果。但是第二种质量压缩之后,传到服务器,在服务端查看会大于想要压缩的内存大小。
原因就是下面这段代码:
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片
这里后的ByteArrayOutputStream 确实达到了指定大小以内,但是又把它转为了Bitmap对象,转换之后的Bitmap对象就变大了,因为bitmap所占内存大小是由图片的宽高和色彩模式Bitmap.Config(代表一个像素占多少个字节)来决定的,Bitmap.Config有下面几种值,Bitmap.Config.ARGB_8888占4个Byte,Bitmap.Config.RGB_565占2个Byte,Bitmap.Config ARGB_4444占2个Byte,Bitmap.Config ALPHA_8占1个Byte,具体计算是:
宽 * 高 * Bitmap.Config所占内存。
找到原因之后,就有解决办法了,我们在对图片进行质量压缩之后,不再转为Bitmap存储,而直接把压缩后的ByteArrayOutputStream存成File来上传。所以对上面的质量压缩方法修改如下:
/**
* 质量压缩
*
* @param srcPath 原图路径
* @param context
* @param max 要压缩到多大以内(单位:kb)
* @return
*/
public String compressReSave(String srcPath, Context context, int max) {
String filePath = "";
Bitmap image = BitmapFactory.decodeFile(srcPath);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中
int options = 90;
while (baos.toByteArray().length / 1024 > max) { // 循环判断如果压缩后图片是否大于maxkb,大于继续压缩
baos.reset(); // 重置baos即清空baos
image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中
if (options > 10){
options -= 10;// 每次都减少10
} else {
options -= 5;
}
if (options == 5){//这里最多压缩到5,options不能小于0
break;
}
}
FileOutputStream outStream = null;
filePath = createFile(context, "myImg");
try {
outStream = new FileOutputStream(filePath);
// 把数据写入文件
outStream.write(baos.toByteArray());
// 记得要关闭流!
outStream.close();
return filePath;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
try {
if (outStream != null) {
// 记得要关闭流!
outStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static String createFile(Context context, String pathName) {
String path = "";
File file = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
file = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + pathName);
} else {
file = new File(context.getFilesDir().getPath() + File.separator + pathName);
}
if (file != null) {
if (!file.exists()) {
file.mkdir();
}
File output = new File(file, System.currentTimeMillis() + ".png");
try {
if (output.exists()) {
output.delete();
} else {
output.createNewFile();
}
path = output.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
}
return path;
}