現在手機拍照都是好幾兆,直接把原圖上傳到伺服器,對於使用者來說,直接就崩潰了,在有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