当需要上传图片到服务器上时,需要对图片进行压缩,如果直接上传原图经常出现一些异常,比如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";
}
}
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;
}
}
主要功能是实现将传入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+"");