现在手机拍照都是好几兆,直接把原图上传到服务器,对于用户来说,直接就崩溃了,在有WiFi的情况下还好。但当其他用户查看列表时,看一会估计手机都得欠费了。所以在上传图片的时候,要对图片进行压缩。
注意:这里的压缩是指质量压缩,不是尺寸压缩。
比如当我们拍一张照片,要上传到服务器。我们需要先把这个照片,读到内存中,然后进行压缩,最后把压缩后的文件输出。
关于图片的内存了,怎么加载节省内存了,这里就不说了
推荐读者看看这篇文章:http://www.jcodecraeer.com/plus/view.php?aid=3874
以下代码来自ImageLoader框架,我把它decoder图片的代码抽了出来,稍作了一下修改
使用代码
public class MainActivity extends AppCompatActivity {
String imagePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "123.jpg";
String outPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "new.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageDecoder decoder = new ImageDecoder();
final ImageSize imageSize = new ImageSize(400, 800);
final File file = new File(imagePath);
if (file.exists()) {
new Thread() {
@Override
public void run() {
super.run();
try {
Bitmap bitmap = decoder.decode(file, imageSize);
ImageUtils.compressBmpToFile(bitmap, new File(outPath));
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
ImageDecoder 类负责把图片decode成目标大小,然后ImageUtils.compressBmpToFile对图片进行压缩,压缩完之后(压缩到100K以内),输出文件
ImageDecoder.java
public class ImageDecoder {
public Bitmap decode(File file, ImageSize targetSize) throws IOException {
Bitmap decodedBitmap;
ImageFileInfo imageInfo;
InputStream imageStream = getImageStream(file);
try {
imageInfo = defineImageSizeAndRotation(imageStream, file);
imageStream = resetStream(imageStream, file);
BitmapFactory.Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, targetSize);
decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);
} finally {
closeSilently(imageStream);
}
if (decodedBitmap != null) {
decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, targetSize, imageInfo.exif.rotation,
imageInfo.exif.flipHorizontal);
}
return decodedBitmap;
}
private InputStream getImageStream(File res) throws IOException{
return new FileInputStream(res);
}
/**
* 定义image的大小和旋转的度
*/
protected ImageFileInfo defineImageSizeAndRotation(InputStream imageStream, File file)
throws IOException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(imageStream, null, options);
ExifInfo exif;
if (canDefineExifParams(options.outMimeType)) {
exif = defineExifOrientation(file);
} else {
exif = new ExifInfo();
}
return new ImageFileInfo(new ImageSize(options.outWidth, options.outHeight, exif.rotation), exif);
}
private boolean canDefineExifParams(String mimeType) {
return "image/jpeg".equalsIgnoreCase(mimeType);
}
protected ExifInfo defineExifOrientation(File imageUri) {
int rotation = 0;
boolean flip = false;
try {
ExifInterface exif = new ExifInterface(imageUri.getAbsolutePath());
int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
switch (exifOrientation) {
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
flip = true;
case ExifInterface.ORIENTATION_NORMAL:
rotation = 0;
break;
case ExifInterface.ORIENTATION_TRANSVERSE:
flip = true;
case ExifInterface.ORIENTATION_ROTATE_90:
rotation = 90;
break;
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
flip = true;
case ExifInterface.ORIENTATION_ROTATE_180:
rotation = 180;
break;
case ExifInterface.ORIENTATION_TRANSPOSE:
flip = true;
case ExifInterface.ORIENTATION_ROTATE_270:
rotation = 270;
break;
}
} catch (IOException e) {
Log.w("decoder", "Can't read EXIF tags from file [%s]" + imageUri.getAbsolutePath());
}
return new ExifInfo(rotation, flip);
}
/**
* 如果imageStream支持reset,则返回reset后的imageStream,否则返回 {@link #getImageStream(File)}
*/
protected InputStream resetStream(InputStream imageStream, File res) throws IOException {
//http://zhangbo-peipei-163-com.iteye.com/blog/2022460
if (imageStream.markSupported()) {
try {
imageStream.reset();
return imageStream;
} catch (IOException ignored) {
}
}
closeSilently(imageStream);
return getImageStream(res);
}
/**
* 返回计算好 simple size 的Option
*/
protected BitmapFactory.Options prepareDecodingOptions(ImageSize imageSize, ImageSize targetSize) {
int scale = ImageUtils.computeImageSampleSize(imageSize, targetSize);
BitmapFactory.Options decodingOptions = new BitmapFactory.Options();
decodingOptions.inSampleSize = scale;
return decodingOptions;
}
protected Bitmap considerExactScaleAndOrientatiton(Bitmap subsampledBitmap, ImageSize targetSize,
int rotation, boolean flipHorizontal) {
Matrix m = new Matrix();
// 缩小到精确的大小,如果需要
ImageSize srcSize = new ImageSize(subsampledBitmap.getWidth(), subsampledBitmap.getHeight(), rotation);
float scale = ImageUtils.computeImageScale(srcSize, targetSize, false);
if (Float.compare(scale, 1f) != 0) {
m.setScale(scale, scale);
}
// 翻转 bitmap 如果需要
if (flipHorizontal) {
m.postScale(-1, 1);
}
// 选择 bitmap 如果需要
if (rotation != 0) {
m.postRotate(rotation);
}
Bitmap finalBitmap = Bitmap.createBitmap(subsampledBitmap, 0, 0, subsampledBitmap.getWidth(), subsampledBitmap
.getHeight(), m, true);
if (finalBitmap != subsampledBitmap) {
subsampledBitmap.recycle();
}
return finalBitmap;
}
protected static class ExifInfo {
public final int rotation;
public final boolean flipHorizontal;
protected ExifInfo() {
this.rotation = 0;
this.flipHorizontal = false;
}
protected ExifInfo(int rotation, boolean flipHorizontal) {
this.rotation = rotation;
this.flipHorizontal = flipHorizontal;
}
}
protected static class ImageFileInfo {
public final ImageSize imageSize;
public final ExifInfo exif;
protected ImageFileInfo(ImageSize imageSize, ExifInfo exif) {
this.imageSize = imageSize;
this.exif = exif;
}
}
public static void closeSilently(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception ignored) {
}
}
}
}
ImageUtils .java
public class ImageUtils {
private static final int DEFAULT_MAX_BITMAP_DIMENSION = 2048;
private static ImageSize maxBitmapSize;
static {
int[] maxTextureSize = new int[1];
GLES10.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
int maxBitmapDimension = Math.max(maxTextureSize[0], DEFAULT_MAX_BITMAP_DIMENSION);
maxBitmapSize = new ImageSize(maxBitmapDimension, maxBitmapDimension);
}
private ImageUtils() {
}
public static int computeImageSampleSize(ImageSize srcSize, ImageSize targetSize){
final int srcWidth = srcSize.getWidth();
final int srcHeight = srcSize.getHeight();
final int targetWidth = targetSize.getWidth();
final int targetHeight = targetSize.getHeight();
int scale = 1;
scale = Math.min(srcWidth / targetWidth, srcHeight / targetHeight); // min
if (scale < 1) {
scale = 1;
}
scale = considerMaxTextureSize(srcWidth, srcHeight, scale, false);
return scale;
}
/**
* 如果宽度和高度/scale大于max texture size则继续缩小
* @return sample size
*/
private static int considerMaxTextureSize(int srcWidth, int srcHeight, int scale, boolean powerOf2) {
final int maxWidth = maxBitmapSize.getWidth();
final int maxHeight = maxBitmapSize.getHeight();
while ((srcWidth / scale) > maxWidth || (srcHeight / scale) > maxHeight) {
if (powerOf2) {
scale *= 2;
} else {
scale++;
}
}
return scale;
}
public static float computeImageScale(ImageSize srcSize, ImageSize targetSize,
boolean stretch) {
final int srcWidth = srcSize.getWidth();
final int srcHeight = srcSize.getHeight();
final int targetWidth = targetSize.getWidth();
final int targetHeight = targetSize.getHeight();
final float widthScale = (float) srcWidth / targetWidth;
final float heightScale = (float) srcHeight / targetHeight;
final int destWidth;
final int destHeight;
if (widthScale < heightScale) {
destWidth = targetWidth;
destHeight = (int) (srcHeight / widthScale);
} else {
destWidth = (int) (srcWidth / heightScale);
destHeight = targetHeight;
}
float scale = 1;
if ((!stretch && destWidth < srcWidth && destHeight < srcHeight) || (stretch && destWidth != srcWidth && destHeight != srcHeight)) {
scale = (float) destWidth / srcWidth;
}
return scale;
}
public static void compressBmpToFile(Bitmap bmp, File file) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int options = 80;
bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
while (baos.toByteArray().length / 1024 > 100) {
options -= 10;
if (options > 0) {
baos.reset();
bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
} else {
break;
}
}
try {
FileOutputStream fos = new FileOutputStream(file);
byte[] bytes = baos.toByteArray();
fos.write(bytes);
fos.flush();
fos.close();
Log.i("tag", "file.length" + file.length());
} catch (Exception e) {
e.printStackTrace();
}
}
}
ImageSize.java
public class ImageSize {
private final int width;
private final int height;
public ImageSize(int width, int height) {
this.width = width;
this.height = height;
}
public ImageSize(int width, int height, int rotation) {
if (rotation % 180 == 0) {
this.width = width;
this.height = height;
} else {
this.width = height;
this.height = width;
}
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
github地址
https://github.com/wu-liao-de-ren-sheng/ImageCompress