Android_头像裁剪上传(客户端+服务器)

DO
文章转载自:http://www.2cto.com/kf/201501/371520.html

(一)头像裁切、上传服务器(代码)

这里上边的按钮是头像的点击事件,弹出底部的头像选择框,下边的按钮跳到下个页面,进行原图上传

@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.avatarImg:// 更换头像点击事件
        menuWindow = new SelectPicPopupWindow(mContext, itemsOnClick);  
        menuWindow.showAtLocation(findViewById(R.id.mainLayout), 
                Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0); 
        break;
    case R.id.loginBtn://登录按钮跳转事件
        startActivity(new Intent(mContext, UploadActivity.class));
        break;

    default:
        break;
    }
}

弹出窗绑定一个按钮事件

//为弹出窗口实现监听类  
private OnClickListener itemsOnClick = new OnClickListener() {
    @Override
    public void onClick(View v) {
        menuWindow.dismiss();
        switch (v.getId()) {
        // 拍照
        case R.id.takePhotoBtn:
            Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            //下面这句指定调用相机拍照后的照片存储的路径
            takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, 
                    Uri.fromFile(new File(Environment.getExternalStorageDirectory(), IMAGE_FILE_NAME)));
            startActivityForResult(takeIntent, REQUESTCODE_TAKE);
            break;
        // 相册选择图片
        case R.id.pickPhotoBtn:
            Intent pickIntent = new Intent(Intent.ACTION_PICK, null);
            // 如果朋友们要限制上传到服务器的图片类型时可以直接写如:image/jpeg 、 image/png等的类型
            pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, image/*);
            startActivityForResult(pickIntent, REQUESTCODE_PICK);
            break;
        default:
            break;
        }
    }
};

为图像选取返回的接收处理

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {

    switch (requestCode) {
    case REQUESTCODE_PICK:// 直接从相册获取
        try {
            startPhotoZoom(data.getData());
        } catch (NullPointerException e) {
            e.printStackTrace();// 用户点击取消操作
        }
        break;
    case REQUESTCODE_TAKE:// 调用相机拍照
        File temp = new File(Environment.getExternalStorageDirectory() + / + IMAGE_FILE_NAME);
        startPhotoZoom(Uri.fromFile(temp));
        break;
    case REQUESTCODE_CUTTING:// 取得裁剪后的图片
        if (data != null) {
            setPicToView(data);
        }
        break;
    }
    super.onActivityResult(requestCode, resultCode, data);
}

把图片显示出来,然后上传

/**
 * 裁剪图片方法实现
 * @param uri
 */
public void startPhotoZoom(Uri uri) {
    Intent intent = new Intent(com.android.camera.action.CROP);
    intent.setDataAndType(uri, image/*);
    // crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
    intent.putExtra(crop, true);
    // aspectX aspectY 是宽高的比例
    intent.putExtra(aspectX, 1);
    intent.putExtra(aspectY, 1);
    // outputX outputY 是裁剪图片宽高
    intent.putExtra(outputX, 300);
    intent.putExtra(outputY, 300);
    intent.putExtra(return-data, true);
    startActivityForResult(intent, REQUESTCODE_CUTTING);
}

/**
 * 保存裁剪之后的图片数据
 * @param picdata
 */
private void setPicToView(Intent picdata) {
    Bundle extras = picdata.getExtras();
    if (extras != null) {
        // 取得SDCard图片路径做显示
        Bitmap photo = extras.getParcelable(data);
        Drawable drawable = new BitmapDrawable(null, photo);
        urlpath = FileUtil.saveFile(mContext, temphead.jpg, photo);
        avatarImg.setImageDrawable(drawable);

        // 新线程后台上传服务端
        pd = ProgressDialog.show(mContext, null, 正在上传图片,请稍候...);
        new Thread(uploadImageRunnable).start();
    }
}

/**
 * 使用HttpUrlConnection模拟post表单进行文件
 * 上传平时很少使用,比较麻烦
 * 原理是: 分析文件上传的数据格式,然后根据格式构造相应的发送给服务器的字符串。
 */
Runnable uploadImageRunnable = new Runnable() {
    @Override
    public void run() {

        if(TextUtils.isEmpty(imgUrl)){
            Toast.makeText(mContext, 还没有设置上传服务器的路径!, Toast.LENGTH_SHORT).show();
            return;
        }

        Map<string, string=""> textParams = new HashMap<string, string="">();
        Map<string, file=""> fileparams = new HashMap<string, file="">();

        try {
            // 创建一个URL对象
            URL url = new URL(imgUrl);
            textParams = new HashMap<string, string="">();
            fileparams = new HashMap<string, file="">();
            // 要上传的图片文件
            File file = new File(urlpath);
            fileparams.put(image, file);
            // 利用HttpURLConnection对象从网络中获取网页数据
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 设置连接超时(记得设置连接超时,如果网络不好,Android系统在超过默认时间会收回资源中断操作)
            conn.setConnectTimeout(5000);
            // 设置允许输出(发送POST请求必须设置允许输出)
            conn.setDoOutput(true);
            // 设置使用POST的方式发送
            conn.setRequestMethod(POST);
            // 设置不使用缓存(容易出现问题)
            conn.setUseCaches(false);
            conn.setRequestProperty(Charset, UTF-8);//设置编码   
            // 在开始用HttpURLConnection对象的setRequestProperty()设置,就是生成HTML文件头
            conn.setRequestProperty(ser-Agent, Fiddler);
            // 设置contentType
            conn.setRequestProperty(Content-Type, multipart/form-data; boundary= + NetUtil.BOUNDARY);
            OutputStream os = conn.getOutputStream();
            DataOutputStream ds = new DataOutputStream(os);
            NetUtil.writeStringParams(textParams, ds);
            NetUtil.writeFileParams(fileparams, ds);
            NetUtil.paramsEnd(ds);
            // 对文件流操作完,要记得及时关闭
            os.close();
            // 服务器返回的响应吗
            int code = conn.getResponseCode(); // 从Internet获取网页,发送请求,将网页以流的形式读回来
            // 对响应码进行判断
            if (code == 200) {// 返回的响应码200,是成功
                // 得到网络返回的输入流
                InputStream is = conn.getInputStream();
                resultStr = NetUtil.readString(is);
            } else {
                Toast.makeText(mContext, 请求URL失败!, Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        handler.sendEmptyMessage(0);// 执行耗时的方法之后发送消给handler
    }
};

Handler handler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
        case 0:
            pd.dismiss();

            try {
                // 返回数据示例,根据需求和后台数据灵活处理
                // {status:1,statusMessage:上传成功,imageUrl:http://120.24.219.49/726287_temphead.jpg}
                JSONObject jsonObject = new JSONObject(resultStr);

                // 服务端以字符串“1”作为操作成功标记
                if (jsonObject.optString(status).equals(1)) {
                    BitmapFactory.Options option = new BitmapFactory.Options();
                    // 压缩图片:表示缩略图大小为原始图片大小的几分之一,1为原图,3为三分之一
                    option.inSampleSize = 1;

                    // 服务端返回的JsonObject对象中提取到图片的网络URL路径
                    String imageUrl = jsonObject.optString(imageUrl);
                    Toast.makeText(mContext, imageUrl, Toast.LENGTH_SHORT).show();
                }else{
                    Toast.makeText(mContext, jsonObject.optString(statusMessage), Toast.LENGTH_SHORT).show();
                }

            } catch (JSONException e) {
                e.printStackTrace();
            }

            break;

        default:
            break;
        }
        return false;
    }
});

**

(二)普通图片上传服务器(代码)

**
直接从这里开始,和头像那里基本没什么区别,我把拍照什么的单独抽出了方法,思路更清晰

//为弹出窗口实现监听类  
private OnClickListener itemsOnClick = new OnClickListener() {
    @Override
    public void onClick(View v) {
        // 隐藏弹出窗口
        menuWindow.dismiss();

        switch (v.getId()) {
        case R.id.takePhotoBtn:// 拍照
            takePhoto();
            break;
        case R.id.pickPhotoBtn:// 相册选择图片
            pickPhoto();
            break;
        case R.id.cancelBtn:// 取消
            break;
        default:
            break;
        }
    }
};
/**
 * 拍照获取图片
 */
private void takePhoto() {
    // 执行拍照前,应该先判断SD卡是否存在
    String SDState = Environment.getExternalStorageState();
    if (SDState.equals(Environment.MEDIA_MOUNTED)) {

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        /***
         * 需要说明一下,以下操作使用照相机拍照,拍照后的图片会存放在相册中的 
         * 这里使用的这种方式有一个好处就是获取的图片是拍照后的原图
         * 如果不使用ContentValues存放照片路径的话,拍照后获取的图片为缩略图不清晰
         */
        ContentValues values = new ContentValues();
        photoUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, photoUri);
        startActivityForResult(intent, SELECT_PIC_BY_TACK_PHOTO);
    } else {
        Toast.makeText(this, 内存卡不存在, Toast.LENGTH_LONG).show();
    }
}

/***
 * 从相册中取图片
 */
private void pickPhoto() {
    Intent intent = new Intent();
    // 如果要限制上传到服务器的图片类型时可以直接写如:image/jpeg 、 image/png等的类型
    intent.setType(image/*);
    intent.setAction(Intent.ACTION_GET_CONTENT);
    startActivityForResult(intent, SELECT_PIC_BY_PICK_PHOTO);
}

处理一下图片选取的页面回调

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // 点击取消按钮
        if(resultCode == RESULT_CANCELED){
            return;
        }

        // 可以使用同一个方法,这里分开写为了防止以后扩展不同的需求
        switch (requestCode) {
        case SELECT_PIC_BY_PICK_PHOTO:// 如果是直接从相册获取
            doPhoto(requestCode, data);
            break;
        case SELECT_PIC_BY_TACK_PHOTO:// 如果是调用相机拍照时
            doPhoto(requestCode, data);
            break;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

接下来就是显示图片和上传服务器了,上传和头像是同一个流程,只是不进行裁切

/**
 * 选择图片后,获取图片的路径
 * 
 * @param requestCode
 * @param data
 */
private void doPhoto(int requestCode, Intent data) {

    // 从相册取图片,有些手机有异常情况,请注意
    if (requestCode == SELECT_PIC_BY_PICK_PHOTO) {
        if (data == null) {
            Toast.makeText(this, 选择图片文件出错, Toast.LENGTH_LONG).show();
            return;
        }
        photoUri = data.getData();
        if (photoUri == null) {
            Toast.makeText(this, 选择图片文件出错, Toast.LENGTH_LONG).show();
            return;
        }
    }

    String[] pojo = { MediaColumns.DATA };
    // The method managedQuery() from the type Activity is deprecated
    //Cursor cursor = managedQuery(photoUri, pojo, null, null, null);
    Cursor cursor = mContext.getContentResolver().query(photoUri, pojo, null, null, null);
    if (cursor != null) {
        int columnIndex = cursor.getColumnIndexOrThrow(pojo[0]);
        cursor.moveToFirst();
        picPath = cursor.getString(columnIndex);

        // 4.0以上的版本会自动关闭 (4.0--14;; 4.0.3--15)
        if (Integer.parseInt(Build.VERSION.SDK) < 14) {
            cursor.close();
        }
    }

    // 如果图片符合要求将其上传到服务器
    if (picPath != null && (    picPath.endsWith(.png) || 
                                picPath.endsWith(.PNG) || 
                                picPath.endsWith(.jpg) || 
                                picPath.endsWith(.JPG))) {


        BitmapFactory.Options option = new BitmapFactory.Options();
        // 压缩图片:表示缩略图大小为原始图片大小的几分之一,1为原图
        option.inSampleSize = 1;
        // 根据图片的SDCard路径读出Bitmap
        Bitmap bm = BitmapFactory.decodeFile(picPath, option);
        // 显示在图片控件上
        picImg.setImageBitmap(bm);

        pd = ProgressDialog.show(mContext, null, 正在上传图片,请稍候...);
        new Thread(uploadImageRunnable).start();
    } else {
        Toast.makeText(this, 选择图片文件不正确, Toast.LENGTH_LONG).show();
    }

}

/**
 * 使用HttpUrlConnection模拟post表单进行文件
 * 上传平时很少使用,比较麻烦
 * 原理是: 分析文件上传的数据格式,然后根据格式构造相应的发送给服务器的字符串。
 */
Runnable uploadImageRunnable = new Runnable() {
    @Override
    public void run() {

        if(TextUtils.isEmpty(imgUrl)){
            Toast.makeText(mContext, 还没有设置上传服务器的路径!, Toast.LENGTH_SHORT).show();
            return;
        }

        Map<string, string=""> textParams = new HashMap<string, string="">();
        Map<string, file=""> fileparams = new HashMap<string, file="">();

        try {
            // 创建一个URL对象
            URL url = new URL(imgUrl);
            textParams = new HashMap<string, string="">();
            fileparams = new HashMap<string, file="">();
            // 要上传的图片文件
            File file = new File(picPath);
            fileparams.put(image, file);
            // 利用HttpURLConnection对象从网络中获取网页数据
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            // 设置连接超时(记得设置连接超时,如果网络不好,Android系统在超过默认时间会收回资源中断操作)
            conn.setConnectTimeout(5000);
            // 设置允许输出(发送POST请求必须设置允许输出)
            conn.setDoOutput(true);
            // 设置使用POST的方式发送
            conn.setRequestMethod(POST);
            // 设置不使用缓存(容易出现问题)
            conn.setUseCaches(false);
            // 在开始用HttpURLConnection对象的setRequestProperty()设置,就是生成HTML文件头
            conn.setRequestProperty(ser-Agent, Fiddler);
            // 设置contentType
            conn.setRequestProperty(Content-Type, multipart/form-data; boundary= + NetUtil.BOUNDARY);
            OutputStream os = conn.getOutputStream();
            DataOutputStream ds = new DataOutputStream(os);
            NetUtil.writeStringParams(textParams, ds);
            NetUtil.writeFileParams(fileparams, ds);
            NetUtil.paramsEnd(ds);
            // 对文件流操作完,要记得及时关闭
            os.close();
            // 服务器返回的响应吗
            int code = conn.getResponseCode(); // 从Internet获取网页,发送请求,将网页以流的形式读回来
            // 对响应码进行判断
            if (code == 200) {// 返回的响应码200,是成功
                // 得到网络返回的输入流
                InputStream is = conn.getInputStream();
                resultStr = NetUtil.readString(is);
            } else {
                Toast.makeText(mContext, 请求URL失败!, Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        handler.sendEmptyMessage(0);// 执行耗时的方法之后发送消给handler
    }
};

Handler handler = new Handler(new Handler.Callback() {

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
        case 0:
            pd.dismiss();

            try {
                JSONObject jsonObject = new JSONObject(resultStr);
                // 服务端以字符串“1”作为操作成功标记
                if (jsonObject.optString(status).equals(1)) {

                    // 用于拼接发布说说时用到的图片路径
                    // 服务端返回的JsonObject对象中提取到图片的网络URL路径
                    String imageUrl = jsonObject.optString(imageUrl);
                    // 获取缓存中的图片路径
                    Toast.makeText(mContext, imageUrl, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(mContext, jsonObject.optString(statusMessage), Toast.LENGTH_SHORT).show();
                }

            } catch (JSONException e) {
                e.printStackTrace();
            }
            break;
        default:
            break;
        }
        return false;
    }
});
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是优化后的代码,主要是添加了一些错误检查和优化了内存拷贝操作: ```cpp void Camera3OutputStream::cropI420(char *src_i420_data, int width, int height, char *dst_i420_data, int dst_width, int dst_height, int left, int top) { if (left < 0 || top < 0 || dst_width <= 0 || dst_height <= 0) { return; } int right = left + dst_width; int bottom = top + dst_height; if (right > width || bottom > height) { return; } int src_y_size = width * height; int src_uv_size = src_y_size / 4; int dst_y_size = dst_width * dst_height; int dst_uv_size = dst_y_size / 4; unsigned char *src_y_data = (unsigned char *)src_i420_data; unsigned char *src_u_data = (unsigned char *)src_i420_data + src_y_size; unsigned char *src_v_data = (unsigned char *)src_i420_data + src_y_size + src_uv_size; unsigned char *dst_y_data = (unsigned char *)dst_i420_data; unsigned char *dst_u_data = (unsigned char *)dst_i420_data + dst_y_size; unsigned char *dst_v_data = (unsigned char *)dst_i420_data + dst_y_size + dst_uv_size; for (int i = 0; i < dst_height; i++) { memcpy(dst_y_data + i * dst_width, src_y_data + (top + i) * width + left, dst_width); } for (int i = 0; i < dst_height / 2; i++) { memcpy(dst_u_data + i * dst_width / 2, src_u_data + ((top + i * 2) * width + left) / 2, dst_width / 2); memcpy(dst_v_data + i * dst_width / 2, src_v_data + ((top + i * 2) * width + left) / 2, dst_width / 2); } } ``` 优化后的代码先进行了一些错误检查,确保裁剪区域在原始图像范围内,同时目标图像的宽度和高度也必须大于0。然后计算出各个数据区域的大小和指针,使用 `memcpy` 函数进行内存拷贝操作。这种方式比使用 libyuv 库中的 `ConvertToI420` 函数更加高效,因为它避免了重复的内存分配和拷贝操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值