您的位置:首页 > 移动开发 > Android开发

Android开发:相册读取、拍照、图片裁剪和图片上传服务器等功能的实现

2016-05-03 16:30 1461 查看
修改日志[b]

2016.05.12

之前的程序存在两个问题:

1)从相册选择的图片如果比较大,会失败;

2)无法拍照上传照片。

修改了这两个bug,之前的代码已经被覆盖掉了,留着太误人子弟了。同时修改了一下标题和文章的文字描述
[/b]

拍照示意





相册示意





一 整体功能描述

二 功能实现
1 获得图片

2上传到服务器并保存

3从服务器中获得图片并显示

4辅助工具

一. 整体功能描述

整理了一下主要有以下几点功能:

1)获得相册图片

2)通过拍照获得图片

3)裁剪图片

4)将图片上传至服务器

5)从服务器获得图片

二. 功能实现

1.1 获得图片

(1)通常情况下,有以下两种方式:

**从相册中选择图片

这种方式原理比较简单,就是从SDK中获得照片,转成字节再生产Bitmap对象用于显示即可。

**拍照获得图片

拍照获取的图片原理就是先拍照存储,然后再读取,就和从相册中选择图片的原理一样了。

(2)实现:

关于图片的选择和处理,推荐一叶飘舟的文章。

首先是点击头像弹出一个dialog供选择读入图片的方式:

private    CharSequence []its = {"拍照","从相册选择"};
public static final int TAKE_PHOTO = 1;//拍照
public static final int CROP_PHOTO = 2;//裁剪
public static final int SELECT_PIC = 0;//从相册选择
private Uri imageUri; //图片路径
private String filename; //图片名称
//上传头像
headImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new AlertDialog.Builder(ModiUserInfoActivity.this)
.setTitle("更换头像")
.setItems(its, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
//最好自己封装一下,方便复用
switch (which)
{
case 0://拍照
//图片名称 时间命名
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = new Date(System.currentTimeMillis());
filename = format.format(date);
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
File outputImage = new File(path,filename+".jpg");
try {
if(outputImage.exists())
{
outputImage.delete();
}
outputImage.createNewFile();
} catch(IOException e) {
e.printStackTrace();
}
//将File对象转换为Uri并启动照相程序
imageUri = Uri.fromFile(outputImage);
Intent tTntent = new Intent("android.media.action.IMAGE_CAPTURE"); //照相
tTntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); //指定图片输出地址
startActivityForResult(tTntent,TAKE_PHOTO); //启动照相
break;
case 1://从相册选择
intent.setAction(Intent.ACTION_GET_CONTENT);//ACTION_OPEN_DOCUMENT
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
intent.putExtra("return-data", true);
intent.putExtra("crop", "true");
//设置宽高比例

intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
//设置裁剪图片宽高、
intent.putExtra("outputX", 450);
intent.putExtra("outputY", 450);
startActivityForResult(intent,SELECT_PIC);
break;
}
}
})
.create()
.show();
}
});
}


然后就是对图片数据进行处理了

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK)
return;
switch (requestCode)
{
case SELECT_PIC://相册
photo = data.getParcelableExtra("data");
if(photo!=null)
{
headImage = ImageDeal.toRoundBitmap(photo);//裁剪成圆形
//上传到服务器...
UserController.updateHeadImage(userId, headImage, handler);
photo.recycle();
}
break;
case SELECT_CAMERA://相机
try {
Intent intent = new Intent("com.android.camera.action.CROP"); //剪裁
intent.setDataAndType(imageUri, "image/*");
intent.putExtra("scale", true);
//设置宽高比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
//设置裁剪图片宽高
intent.putExtra("outputX", 450);
intent.putExtra("outputY", 450);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
Toast.makeText(ModiUserInfoActivity.this, "剪裁图片", Toast.LENGTH_SHORT).show();
//广播刷新相册
Intent intentBc = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intentBc.setData(imageUri);
this.sendBroadcast(intentBc);
startActivityForResult(intent, CROP_PHOTO); //设置裁剪参数显示图片至ImageView
break;
} catch (Exception e) {
e.printStackTrace();
}
break;
case CROP_PHOTO:
try {
//图片解析成Bitmap对象
Bitmap bitmap = BitmapFactory.decodeStream(
getContentResolver().openInputStream(photoFile));

headImage = ImageDeal.toRoundBitmap(bitmap);
UserController.updateHeadImage(userId, headImage, handler);
} catch(FileNotFoundException e) {
e.printStackTrace();
}
break;
default:
break;
}

}


ok,按照上面的代码,从相册选择照片和拍照 裁剪就没有问题了,至于对图片的其他一些处理,可以参考一叶飘舟的博文。

1.2上传到服务器并保存

查阅了很多资料,使用的方式要么都是那种上传文件那类看起来就特别复杂的方式,看的我真的头大了。后来找了好几个版本自己都没能成功实现把1.1中获取的图片上传到服务器。后来回想了一下http请求不就是传送的字节嘛,那我把图片转成字节不就能传过去了么,另一个问题又来了,有时候我们并不是单单传送一张图片过去,可能还有其他信息,比如一个表单或者一个id之类的,总之要传送的数据还有不仅仅是图片。之前传送数据都是通过json或者map转成String再转成byte[]实现的,那么现在传送的是图片+其他数据,思路便是:图片->String,然后和其他数据一起打包成json->String->byte[]->Bitmap。

客户端

现在接着写1.1中代码使用到的UserController.updateHeadImage(userId, headImage, handler)。

/**
* 保存头像
* @param userId
* @param handler
* @param image
*/
public static void updateHeadImage(final String userId,final Bitmap image, final Handler handler)
{
new Thread()
{
@Override
public void run()
{
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("userId",userId);
//将Bitmap转成String,其实这是一个加密过程。后面会有Common.Bitmap2String()的代码。
jsonObject.put("userImageContent", Common.Bitmap2String(image));

String content = String.valueOf(jsonObject);
/**
* 请求地址
*/
String url =  ConfigModel.getServerURL()+"user/updateImage";
String result = Common.httpPost(url,content);//post请求服务器:数据content,请求路径url。这个函数也是自己写在Common类里,其实就是稍微封装了一下post请求的过程,方便复用。
Log.i("result",result);
/**
* 服务器返回结果
* 继续干什么事情....
*/
Message message = new Message();
Bundle bundle = new Bundle();
bundle.putString("result",result);
message.setData(bundle);
message.what = R.id.save_user_image_result;
handler.sendMessage(message);

}catch (Exception e){}
}
}.start();
}


服务器端

客户端将图片加密成了String,那么服务端就需要解码就可以获得图片。

//第一步,将数据流转String,自己封装成了一个read函数,方便复用;
String streamIn = ReadStream.read(new BufferedInputStream(request.getInputStream()));
//JSONObject object = JSONObject.fromObject(streamIn).getJSONObject("user");
JSONObject object = JSONObject.fromObject(streamIn);//String转JSON
String userId = object.getString("userId");
String userImageContent = object.getString("userImageContent");//获得图像的数据
//..其他步骤省略
//..比如判断是否是新图像,比如生成图像ID imgId = Tool.getUUID();
//第二步将图像数据String转成Bitmap
byte[] bitmapArray= Base64.decode(imageContent);
try
{
File imageFile = new File(headImagePath);
if(!imageFile.exists())
imageFile.mkdirs();
//创建输出流
FileOutputStream outStream = new FileOutputStream(imageFile.getPath()+"\\"+imgId+".png");
//写入数据
outStream.write(bitmapArray);
//关闭输出流
outStream.close();
}
catch(IOException e)
{
System.out.println(e);
}


由此就完美的将客户端的图像和数据都上传到服务器上了。下面部分就是从服务器上获得图片和数据再返回给客户端,有了前面部分的思路,那这部分就很容易实现,反其道而行之就可以了。

1.3从服务器中获得图片并显示

服务器端

服务器端要做的事情就是将Bitmap转成String,然后和其他数据打包成json,返回给客户端。

//读取图片,转成String
public static String readImage(String imgId)
{
//byte []data = imageContent.getBytes();
File file = new File(headImagePath+imgId+".png");
try {
FileInputStream inputStream = new FileInputStream(file);
byte[] bitmapArray = new byte[(int) file.length()];
inputStream.read(bitmapArray);
inputStream.close();
return Base64.encode(bitmapArray);
} catch(Exception e)
{
return "";
}
}
//*****************************************
//代码片.....和上面的代码不在同一个java文件中
//*****************************************
/**
* 查询一个user
*
* @param userId
*            主键
* @return user
*/
@RequestMapping(value = "/user/get/{id}")
@ResponseBody
public JSONObject get(@PathVariable("id") String userId,
HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*");
User user = userDAO.getUser(userId);
JSONObject jsonObject = new JSONObject();
jsonObject.put("user", user);
if(user.getUserImage()!=null&&!"".equals(user.getUserImage()))
{
jsonObject.put("userImageContent", Tool.readImage(user.getUserImage()));
}
return jsonObject;
}


客户端

这部分我就不贴代码了,因为自己把很多功能都封装了,要贴出来有点麻烦,总之此时的图片数据已经转成了String,只需要在显示的时候,再转成Bitmap。String->Bitmap的实现请参考我的Common类。

1.4辅助工具

/**
* 图像转字节
* @param bm
* @return
*/
public static byte[] Bitmap2Bytes(Bitmap bm) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
}

/**
* 图像转String
* @param bitmap
* @return
*/
public static String Bitmap2String(Bitmap bitmap)
{
return Base64.encodeToString(Bitmap2Bytes(bitmap), Base64.DEFAULT);
}
/**
* string转成bitmap
*
* @param st
*/
public static Bitmap String2Bitmap(String st)
{
Bitmap bitmap = null;
try
{
byte[] bitmapArray;
bitmapArray = Base64.decode(st, Base64.DEFAULT);
bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.length);
return bitmap;
}
catch (Exception e)
{
return null;
}
}
/**
* 把bitmap转成圆形
* */
public static Bitmap toRoundBitmap(Bitmap bitmap){
int width=bitmap.getWidth();
int height=bitmap.getHeight();
int r=0;
//取最短边做边长
if(width<height){
r=width;
}else{
r=height;
}
//构建一个bitmap
Bitmap backgroundBm= Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
//new一个Canvas,在backgroundBmp上画图
Canvas canvas=new Canvas(backgroundBm);
Paint p=new Paint();
//设置边缘光滑,去掉锯齿
p.setAntiAlias(true);
RectF rect=new RectF(0, 0, r, r);
//通过制定的rect画一个圆角矩形,当圆角X轴方向的半径等于Y轴方向的半径时,
//且都等于r/2时,画出来的圆角矩形就是圆形
canvas.drawRoundRect(rect, r/2, r/2, p);
//设置当两个图形相交时的模式,SRC_IN为取SRC图形相交的部分,多余的将被去掉
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//canvas将bitmap画在backgroundBmp上
canvas.drawBitmap(bitmap, null, rect, p);
return backgroundBm;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: