Android头像上传(本地相册和调用系统相机)

  市面上上的android应用目前大都包含有头像上传的功能,恰好在本次的项目中在完成头像上传的功能中遇到了一些问题,在此进行记录,以供日后查阅方便.


1.所谓头像上传,是要指定你要上传的方式的,比如:调用本地相册,在相册中进行选取(此方法本人是写在了工具类中,所以需要传入activity为参数)

//从相册中选取照片
public void doPickPhotoFromGallery(Activity activity) {
    final Intent intent = getPhotoPickIntent();
    activity.startActivityForResult(intent, Constant.PHOTO_PICKED_WITH_DATA);
}

public Intent getPhotoPickIntent() {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
    intent.setType("image/*");
    intent.putExtra("crop", "true");
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1);
    intent.putExtra("outputX", 80);
    intent.putExtra("outputY", 80);
    intent.putExtra("return-data", true);
    return intent;
}

2.再比如,你想要调用系统的相机来进行拍照(声明了两个全局变量)

/*拍照的照片存储位置*/
private static final File PHOTO_DIR = new File(Environment.getExternalStorageDirectory() + "/DCIM/Camera");

public File mCurrentPhotoFile;//照相机拍照得到的图片

 //打开系统相册
public void openSystemCamera(Activity activity) {
    String status = Environment.getExternalStorageState();
    if (status.equals(Environment.MEDIA_MOUNTED)) {//判断是否有SD卡
        doTakePhoto(activity);// 用户点击了从照相机获取
    } else {
        Toast.makeText(activity, "未装载内存卡", Toast.LENGTH_SHORT).show();
    }
}
  /**
 * 拍照获取图片
 */
public void doTakePhoto(Activity activity) {
    try {
        // Launch camera to take photo for selected contact
        PHOTO_DIR.mkdirs();// 创建照片的存储目录
        mCurrentPhotoFile = new File(PHOTO_DIR, getPhotoFileName());// 给新照的照片文件命名
        Intent intent = getTakePickIntent(mCurrentPhotoFile);
        intent.putExtra(Constant.CURRENT_PHOTO_FILE, mCurrentPhotoFile);
        activity.startActivityForResult(intent, Constant.CAMERA_WITH_DATA);
    } catch (Exception e) {
        Log.e("TAG", "开启系统相机出问题啦");
    }
}

 public Intent getTakePickIntent(File f) {
    final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE, null);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f));
    return intent;
}

 /**
 * 用当前时间给取得的图片命名
 */
private String getPhotoFileName() {
    Date date = new Date(System.currentTimeMillis());
    SimpleDateFormat dateFormat = new SimpleDateFormat(
            "'IMG'_yyyy-MM-dd HH:mm:ss");
    return dateFormat.format(date) + ".jpg";
}

3.到这里已经可以获取到选择的图片信息了,下面该对图片进行编辑了,截取图片中的某一块区域(这里也是写在工具类中的,所以照例还是传入了activity参数)


(1)在图库中选取:
从MemberActivity界面–>到图库的选择界面–>选择某一个图片的某个区域–>携带bitmap的对象返回MemberActivity(在onAcitivityResult中取到了bitmap对象,具体中间的操作过程我也不是很清楚….)

(2)调用系统相机进行拍照:
从MemberActivity界面–>系统相机界面–>拍照并确认–>回到MemberActivity的onActivityResult方法中,并走case Constant.CAMERA_WITH_DATA的流程(在这里需要对intent进行非空判断,如果不进行判断的话会报空指针的错误,具体为什么intent会为空就不得而知了,如果intent为null的话,就将File file置为null,并传入下一步的doCropPhoto(),如果intent不为空,则获取file传入的值,再将这个值传入到doCropPhoto()中去处理)–> doCropPhoto()对图片进行区域的选择(相应的,这一步要判断file值是否为空,因为上一步的传入过程中存在file为空的情况)–>开启系统的图库去对图片进行区域的选择–>携带这选择的bitmap返回MemberActivity,并执行onActivityResult的方法.

protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    if (resultCode != RESULT_OK)
        return;
    switch (requestCode) {
        //调用了从图库选取的功能的时候会直接走这个流程
        case Constant.PHOTO_PICKED_WITH_DATA: {// 调用Gallery返回的
            //在控件中显示得到的bitmap
           Bitmap photo = intent.getParcelableExtra("data");
               break;
        }
        case Constant.CAMERA_WITH_DATA: {// 照相机程序返回的,再次调用图片剪辑程序去修剪图片
            File file = null;
            if (intent != null) {
                file = (File) intent.getSerializableExtra(Constant.CURRENT_PHOTO_FILE);
            }
            SPUtils.getInstance().doCropPhoto(file, MemberSettingActivity.this);
            break;
        }
    }
}


public void doCropPhoto(File f, Activity activity) {
    Intent intent = null;
    if (f == null) {
        intent = getCropImageIntent(Uri.fromFile(mCurrentPhotoFile));
    } else {
        intent = getCropImageIntent(Uri.fromFile(f));
    }
    try {
        // 启动gallery去剪辑这个照片
        activity.startActivityForResult(intent, Constant.PHOTO_PICKED_WITH_DATA);
    } catch (Exception e) {

    }
}

 /**
 * Constructs an intent for image cropping. 调用图片剪辑程序
 */
public Intent getCropImageIntent(Uri photoUri) {
    Intent intent = new Intent("com.android.camera.action.CROP");
    intent.setDataAndType(photoUri, "image/*");
    intent.putExtra("crop", "true");
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1);
    intent.putExtra("outputX", 80);
    intent.putExtra("outputY", 80);
    intent.putExtra("return-data", true);
    return intent;
}

3.顺利的拿到bitmap的对象了,现在我们该做的就是把图片上传到云存储中去了,我在这里用的是七牛云存储,鉴于现在所在的公司条件有限,在没有后台的情况下,上传图片到七牛的过程中是遇到了一些问题的,下面简单的介绍一下遇到的问题.

3.1.首先,在查阅了七牛中关于android上传图片的文档中,关于android上传图片的介绍是非常简单的,操作起来也确实不是很复杂.大概用到的代码如下:

//在Application中对其进行的初始化(我是这么做的,至于在别的地方行不行,我没试)
    Configuration config = new Configuration.Builder()
            .chunkSize(256 * 1024)  //分片上传时,每片的大小。 默认256K
            .putThreshhold(512 * 1024)  // 启用分片上传阀值。默认512K
            .connectTimeout(10) // 链接超时。默认10秒
            .responseTimeout(60) // 服务器响应超时。默认60秒
            .recorder(recorder)  // recorder分片上传时,已上传片记录器。默认null
            .recorder(recorder, keyGen)  //keyGen 分片上传时,生成标识符,用于片记录器区分是那个文件的上传记录
            .zone(Zone.zone0) // 设置区域,指定不同区域的上传域名、备用域名、备用IP。
            .build();

    // 重用uploadManager。一般地,只需要创建一个uploadManager对象
    UploadManager uploadManager = new UploadManager(config);

    data = <File对象、或 文件路径、或 字节数组>
    String key = <指定七牛服务上的文件名,或 null>;
    String token = <从服务端SDK获取>;
    uploadManager.put(data, key, token,
    new UpCompletionHandler() {
        @Override
        public void complete(String key, ResponseInfo info, JSONObject res) {
            //这里就是上传完成以后的回调(应该是吧?我是这么理解的,不过我不确定)

            }
        }, null);

3.2 传图片到服务器上基本上就是上面的这几行代码,其实真正在进行上传操作的只有最后的几行,就是uploadManager.putxxxx这些,当然了,如果用过的话,可能会对其中需要的几个参数比较了解,但初次接触的话可能确实会有些疑惑(反正我是懵逼了,尤其是在没有后台的情况下).
key:就是你存入七牛云的文件的名称,例如: ceshi.jpg(这个名字就看你自己想叫什么了,阿猫阿狗的也行)
token:这里是需要经过一定的算法以及需要你传一些你的七牛帐号的参数的,如果有后台的话,可以让他返回给你.而如果巧的是没有后台的话,这里是需要你自己进行获取的.(这个值不正确的话是无法上传图片的)

获取token的过程:在七牛的官网上下载java的sdk–>将其中util目录下的auth.java文件复制到你自己的工程下–>在把auth.java复制过来以后,会有报错,这时候把java的sdk下相关的类一起复制过来就可以了(这里就不详细的说了,就那么几个工具类,我相信大家都可以的).当然在完成这些以后,获取token值也变得比较简单了:

Auth auth = Auth.create(七牛个人账户的AccessKey,七牛个人账户的SecretKey);
//相当于在你的七牛帐号下新建的一个文件夹吧,这个名称貌似是唯一的,就是你的和别人的也不能重复(我也不确定,猜的)
String token = auth.uploadToken(七牛个人账户下的某个空间名称);

3.3 上传七牛的参数拿到了,下面就是上传我们的bitmap对象了,因为上传到七牛的数据这是有限制的,所以我们先把我们的bitmap转成一个数组,然后进行上传的操作.(这里的photo就是上面的bitmap对象,我把他声明成了一个全局变量)

ByteArrayOutputStream baos = new ByteArrayOutputStream();
    photo.compress(Bitmap.CompressFormat.PNG, 100, baos);
    byte[] bytes = baos.toByteArray();
    try {
        baos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    //开始上传我们的图片了
    uploadManager.put(bytes, key, token, new UpCompletionHandler() {
        @Override
        public void complete(String key, ResponseInfo info, JSONObject response) {
            Log.e("TAG", "qiniu" + key + info);
       //我没有在这里调用bitmap.recycle()的方法,是因为如果我调用的话,再次进行拍照获取图片的话会报一个什么什么什么的错误,我也不知道具体是什么
        }
    }, null);

4.最后,在MemberActivity的onDestroy()中,加上了photo.recycle()的方法,其实我也不知道有没有什么卵用.基本上就到此为止了.当然,本人对于android的一些知识理解尚浅,欢迎大牛们予以指点,谢谢!