本文主要记录这几天项目用到的用户头像上传功能实现的几个比较重要的知识点,即实现Android端向服务器端上传图片文件。
首先是效果图:
1、选择本地相册缩略图或者打开照相机,获取本地图片路径
从本地相册或者照相机获取图片
具体代码如下:
从当前相册中获取具体相片路径:
private void pickPhoto() {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, SELECT_IMAGE_RESULT_CODE);
}
打开照相机,拍摄照片并把照片保存到指定路径:
private void takePhoto() {
// 执行拍照前,应该先判断SD卡是否存在
String SDState = Environment.getExternalStorageState();
if (SDState.equals(Environment.MEDIA_MOUNTED)) {
//通过指定图片存储路径,解决部分机型onActivityResult回调 data返回为null的情况
//获取与应用相关联的路径
String imageFilePath = getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA);
//根据当前时间生成图片的名称
String timestamp = "/"+formatter.format(new Date())+".png";
File imageFile = new File(imageFilePath,timestamp);// 通过路径创建保存文件
mImagePath = imageFile.getAbsolutePath();
Uri imageFileUri = Uri.fromFile(imageFile);// 获取文件的Uri
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT,imageFileUri);// 告诉相机拍摄完毕输出图片到指定的Uri
startActivityForResult(intent, SELECT_IMAGE_RESULT_CODE);
} else {
Toast.makeText(this, "内存卡不存在!", Toast.LENGTH_LONG).show();
}
}
得到用户选择的图片路径:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == SELECT_IMAGE_RESULT_CODE && resultCode == RESULT_OK){
String imagePath = "";
if(data != null && data.getData() != null){
//有数据返回直接使用返回的图片地址
imagePath = getFilePathByFileUri(this, data.getData());
}else{//无数据使用指定的图片路径
imagePath = mImagePath;
}
dataList.addFirst(imagePath);
adapter.update(dataList); // 刷新图片
}
}
/**
* 根据文件Uri获取路径
*/
public static String getFilePathByFileUri(Context context, Uri uri) {
String filePath = null;
Cursor cursor = context.getContentResolver().query(uri, null, null,
null, null);
if (cursor.moveToFirst()) {
filePath = cursor.getString(cursor
.getColumnIndex(MediaStore.Images.Media.DATA));
}
cursor.close();
return filePath;
}
2、获取图片之后,根据项目对用户头像大小的要求,对选择的图片进行缩放处理:
对图片压缩有两点:
(1)节省用户流量,缩短上传时间。
(2)防止OOM异常的发生
/**
* 从指定路径读取图片文件,并进行缩放
* @param context
* @param imgPath
* @return
*/
private static Bitmap readBitmap(Context context, String imgPath) {
try {
Options opts = new Options();
// 如果设置为true,仅仅返回图片的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(imgPath, opts);
// 得到原图的宽和高
int srcWidth = opts.outWidth;
int srHeigth = opts.outHeight;
//获取缩放的比例(我这里设置的是80dp)
int scale = 1;
int sx = srcWidth/dip2px(context, 80); // 3000/320= 9;
int sy = srHeigth/dip2px(context, 80); // 2262/480= 5;
if(sx > sy && sx > 1){
scale = sx;
}
if(sy > sx && sy > 1){
scale = sy;
}
// 设置为false表示,让decodeFile返回一个bitmap类型的对象
opts.inJustDecodeBounds = false;
// 设置缩放的比例,设置后,decodeFile方法就会按照这个比例缩放图片,加载到内存
opts.inSampleSize= scale;
// 缩放图片加载到内存(一般加载图片到内存,都要根据手机屏幕宽高对图片进行适当缩放,否则可能出现OOM异常)
Bitmap bitmap = BitmapFactory.decodeFile(imgPath, opts);
return bitmap;
} catch (Exception e) {
// TODO Auto-generated catch block
return null;
}
}
/**
* dp转换成px
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
3、把获取到的缩放图片转换为Base64编码,然后赋值给字符串:
/**
* 根据图片路径和格式把图片转化为Base64编码格式
* @param imgPath
* @param bitmap
* @param imgFormat 图片格式
* @param quality 压缩的程度
* @return
*/
public static String imgToBase64(Context context, String imgPath, String imgFormat, int quality) {
Bitmap bitmap = null;
if (imgPath !=null && imgPath.length() > 0) {
bitmap = readBitmap(context,imgPath);
}
if(bitmap == null){
return null;
}
ByteArrayOutputStream out = null;
try {
out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, quality, out);
out.flush();
out.close();
byte[] imgBytes = out.toByteArray();
long length = imgBytes.length;
LogUtil.LogShitou("字节长度", length+"");
return Base64.encodeToString(imgBytes, Base64.DEFAULT);
} catch (Exception e) {
return null;
} finally {
try {
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4、根据服务端提供的接口,上传图片:
这最后一步,我就不贴代码了,因为每个人用的网络框架和服务端接口应该都是不同的,但是基本思路是不变的,大家可以根据稍作变动来实现自己的需求。如图所示,这是我项目中服务端定义的接口,我在调用这个接口的时候,只需要把用户选择的图片文件经过上面的步骤,最终转换成Base64编码,然后赋值给这个参数,再调用这个接口即可实现用户头像上传了!
最后附带上Demo下载地址:Demo下载地址
这个demo并不是一个完整的项目,主要实现了本地照片的选择功能,里面提供了一个工具类实现了图片的缩放加载,压缩转化为Base64格式字符串,大家可以参考思路。