Android实现图片压缩并且不失真

当需要上传图片到服务器上时,需要对图片进行压缩,如果直接上传原图经常出现一些异常,比如java.net.SocketException: sendto failed: EPIPE (Broken pipe),按照现今市场上的手机像素都比较高,那么大小至少也都是1M以上,对于图片上传这个功能来说,上传原图不仅耗时也没有必要,而且有些手机http不支持上传过大的文件,需要你自己写Socket实现http协议去post,比如我自己用的小米手机直接上传原图就会抛出以上异常。

由于这里主要介绍图片压缩。再将压缩完成之后的bitmap,保存本地,本地就会形成一个新的压缩之后的图片。

上传的过程中选择压缩后的图片便可以上传。

这里主要用到两个工具类,分别是PictureUtil与FileUtils。

PictureUtil的源码如下:

package com.qian.pos.util;

import java.io.ByteArrayOutputStream;
import java.io.File;

import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.util.Base64;

public class PictureUtil {


	public static String bitmapToString(String filePath) {

		Bitmap bm = getSmallBitmap(filePath,480,800);
		
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		bm.compress(Bitmap.CompressFormat.JPEG, 40, baos);
		byte[] b = baos.toByteArray();
		
		return Base64.encodeToString(b, Base64.DEFAULT);
		
	}
	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 = heightRatio < widthRatio ? heightRatio : widthRatio;
		}

		return inSampleSize;
	}

	

	
	/**
	 * 根据路径获得突破并压缩返回bitmap用于显示
	 * @param filePath  图片的路径
	 * @param reqWidth  要求的图片的像素
	 * @param reqHeight 要求的图片的像素
	 * @return
	 */
	public static Bitmap getSmallBitmap(String filePath,int reqWidth, int reqHeight) {
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(filePath, options);

		options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

		options.inJustDecodeBounds = false;

		return BitmapFactory.decodeFile(filePath, options);
	}
	public static void deleteTempFile(String path) {
		File file = new File(path);
		if (file.exists()) {
			file.delete();
		}
	}
	public static void galleryAddPic(Context context, String path) {
		Intent mediaScanIntent = new Intent(
				Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
		File f = new File(path);
		Uri contentUri = Uri.fromFile(f);
		mediaScanIntent.setData(contentUri);
		context.sendBroadcast(mediaScanIntent);
	}
	public static File getAlbumDir() {
		File dir = new File(
				Environment
						.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
				getAlbumName());
		if (!dir.exists()) {
			dir.mkdirs();
		}
		return dir;
	}
	public static String getAlbumName() {
		return "sheguantong";
	}
}

我们在主程序调用的方法是public static Bitmap getSmallBitmap(String filePath,int reqWidth, int reqHeight);

filePath是传入图片的全路径,reqWidth是压缩后图片的宽度的像素,reqHeight是压缩后图片的高度的像素,


public static Bitmap getSmallBitmap(String filePath,int reqWidth, int reqHeight) {
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(filePath, options);

		options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

		options.inJustDecodeBounds = false;

		return BitmapFactory.decodeFile(filePath, options);
	}

首先创建BitmapFacotry.Options参数,这个参数很重要,因为它的参数配置能够极大的减少你对内存的消耗。

设置参数options.inJustDecodeBounds =true,inJustDecodeBounds参数,置于这个参数什么意思,贴出来google的原文:

  /**
         * If set to true, the decoder will return null (no bitmap), but
         * the out... fields will still be set, allowing the caller to query
         * the bitmap without having to allocate the memory for its pixels.
         */

用我的话来说就是在decode的时候不给这个bitmap的像素区分配内存,除了这个区别Bitmap的其他信息你都能获取到。这样就有很大的意义,你既没有消耗内存又拿到了图片的信息,为你下一步图片处理提供帮助。

然后利用options的参数对图片进行解码。传入option参数,BitmapFactory.decodeFile(filePath, options);

那么执行完这句之后,options参数对象就包含原图的宽高数据了,分别保存在options.outHeight与options.outWidth中,然后调用方法calculateInSampleSize(options, reqWidth, reqHeight)计算采样率,保存在options对象的options.inSampleSize值中,最后利用options参数对原图进行解码,此时options参数包含了采样比例。便可以达到压缩图片的目的,返回bitmap对象。

然后是方法calculateInSampleSize(options, reqWidth, reqHeight)

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 = heightRatio < widthRatio ? heightRatio : widthRatio;
		}

		return inSampleSize;
	}
一般手机的分辨率都有宽高比,可以收集分辨率设置reqWidth,reqHeight这2个值,这2个值只是期望的宽度与高度,实际上压缩后的实际宽度也高度会比期望的要大。如果图片的原始高度或者宽带大约我们期望的宽带和高度,我们需要计算出缩放比例的数值。否则就不缩放。heightRatio是图片原始高度与压缩后高度的倍数,widthRatio是图片原始宽度与压缩后宽度的倍数。inSampleSize为heightRatio与widthRatio中最小的那个,inSampleSize就是缩放值。 inSampleSize为1表示宽度和高度不缩放,为2表示压缩后的宽度与高度为原来的1/2


实际效果是图片被压缩到100KB左右都不会失真,只是分辨率要差一点。可以根据需要设置reqWidth,reqHeight这2个参数。


下面是FileUtils,源码为:

package com.qian.pos.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.graphics.Bitmap;
import android.os.Environment;

public class FileUtils {
	
	public static String SDPATH = Environment.getExternalStorageDirectory()
			+ "/pos/";

	public static void saveBitmap(Bitmap bm, String picName) {
		System.out.println("-----------------------------");
		try {
			if (!isFileExist("")) {
				System.out.println("创建文件");
				File tempf = createSDDir("");
			}
			File f = new File(SDPATH, picName + ".JPEG"); 
			if (f.exists()) {
				f.delete();
			}
			FileOutputStream out = new FileOutputStream(f);
			bm.compress(Bitmap.CompressFormat.JPEG, 90, out);
			out.flush();
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static File createSDDir(String dirName) throws IOException {
		File dir = new File(SDPATH + dirName);
		if (Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {

			System.out.println("createSDDir:" + dir.getAbsolutePath());
			System.out.println("createSDDir:" + dir.mkdir());
		}
		return dir;
	}

	public static boolean isFileExist(String fileName) {
		File file = new File(SDPATH + fileName);
		file.isFile();
		System.out.println(file.exists());
		return file.exists();
	}
	
	public static void delFile(String fileName){
		File file = new File(SDPATH + fileName);
		if(file.isFile()){
			file.delete();
        }
		file.exists();
	}

	public static void deleteDir() {
		File dir = new File(SDPATH);
		if (dir == null || !dir.exists() || !dir.isDirectory())
			return;
		
		for (File file : dir.listFiles()) {
			if (file.isFile())
				file.delete(); 
			else if (file.isDirectory())
				deleteDir(); 
		}
		dir.delete();
	}

	public static boolean fileIsExists(String path) {
		try {
			File f = new File(path);
			if (!f.exists()) {
				return false;
			}
		} catch (Exception e) {

			return false;
		}
		return true;
	}

}

我们在主程序调用的方法是public static void saveBitmap(Bitmap bm, String picName);

主要功能是实现将传入bitmap数据保存为图片在一定的目录下。picName是这个保存图片的名称。

public static void saveBitmap(Bitmap bm, String picName) {
		System.out.println("-----------------------------");
		try {
			if (!isFileExist("")) {
				System.out.println("创建文件");
				File tempf = createSDDir("");
			}
			File f = new File(SDPATH, picName + ".JPEG"); 
			if (f.exists()) {
				f.delete();
			}
			FileOutputStream out = new FileOutputStream(f);
			bm.compress(Bitmap.CompressFormat.JPEG, 90, out);
			out.flush();
			out.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
方法执行的流程为  如果需要保存图片的目录不存在,便创建这个目录,然后再这个目录创建图片文件,将bitmap压缩一下,通过输出流写入到这个文件当中;  实际上我们在之前已经压缩过一次,所以这里也可以不用压缩,这里调用bm.compress()方法进行压缩,这个方法的第二个参数,如果是100,表示不压缩。

主程序执行流程如下:

Bitmap saveBitmap = PictureUtil.getSmallBitmap(picPath,1280,720);//上传服务器的bitmap 手机横着拍照

FileUtils.saveBitmap(saveBitmap, requestCode+"");



你可能感兴趣的:(Android)