android选取本地图片及关于图片压缩上传问题

Android 专栏收录该内容
28 篇文章 0 订阅

<span style="font-size:18px;">在一个项目里面,有一个需求是让用户自己选择图片,然后上传到服务器。看似一个很简单的需求,就是选择图片,把图片装好,然后通过网络请求上传到后台,OK。但是事实并非如此,因为我们可以android项目,他是open的,他有更多的可能性,当然你也会遇到更多古灵精怪的问题。</span>

获取图片有3种方法,一是自己用surface控件,利用镜头来获取图片;二是调用系统相机,并且返回拍到的图片;三是直接在利用图库获取本地图片;这里我只使用后面两种方法来获取图片(系统提供了方法,为什么不用呢,还要费那么大劲去开发一个新的已经有了的功能,不重复造轮子)。在这过程中,我遇到了好几个很奇葩和让人难以理解的问题。弄了我大半天时间,现在做一下记录。

我所遇到的问题大概有以下:

1、利用系统图库获取图片,返回的图片地址,因系统不同而不同;

2、利用拍照返回的照片是经过压缩的,分辨率很低,压根看不清楚;

3、

第一个问题:返回的图片地址不同。为什么这样讲呢,因为我们知道android已经有很多的深度定制的系统,像是小米,华为,魅族,锤子...等等,都对原生的android系统作了修改优化,至少我不知道它们能在底层改了什么东西,我们只有直面底层的返回,去适应它。在这里,我们利用显示intent方式打开本地图库,代码如下

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);//设置动作

<span style="font-size:18px;"><span style="white-space:pre">	</span>intent.setType("image/*");//开启Pictures画面Type设定为image
<span style="white-space:pre">	</span>startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE1);</span>
这样就可以打开系统的图库来选去需要的图片了。

那么,问题就来了。我在魅蓝2上面打开后,系统跳到的是最近的拍照照片,而在小米2s上面也是跳到最近的拍摄的图片集合里面去,在华为honor6 ?上面则是跳到最近编辑过的图片集合里面去了。最后在onactivityresult()回调方法里面利用以下代码得到图片地址:

<span style="font-size:18px;"><span style="white-space:pre">	</span>Uri uri = data.getData();
        String pathImg = uri.getPath();</span>
魅蓝获取到2获取到的图片路径是:/external/images/media/640543,小米2s的路径是:/storage/sdcard0/DCIM/Camera/IMG_20160217_142658.jpg,华为的类似魅蓝2的路径,这里没有打印出来。在这里我要的是选取图片的完整路径,如果不用不需要完整路径,也是可以获取到图片的,代码如下:

Bitmap bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(uri)); 这个方法返回来的是一个bitmap,一看到bitmap应该立即想到的是oom,这是一个程序猿又爱又恨的东西啊。现在的手机随便拍个照,都有1000(+)*1000(+)的像素,在android里面一个像素点,用4个字节存放。所以,读入一个1000*1000像素的图片,读入来,占用的字节数有1000*1000*4.这对一个程序来说,几乎是致命的。这样的做法显然不可取。
	在上面返回的路径看来,小米2s返回的路径是对的,但是我的代码还要在魅蓝上运行,显然也是不通过的。所以我又找了另外一个办法来获取返回的图片路径:
String picturePath = "";
        String[] proj = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(uri, proj, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            int columnIndex = cursor.getColumnIndex(proj[0]);
            picturePath = cursor.getString(columnIndex);
            System.out.println("-=-==->>picturePath = " + picturePath);
        }
此方法,在华为honor6?上面返回的的cursor为null,魅蓝上是OK的,小米的也是null。这就让人很费解了。同样是显示调用系统的方法,但是返回来的却是截然不同的东西。而我的目标是要不oom和完整的路径。
	所以在这里,我用了一个猥琐的方法来绕过这个坑。就是我同时用两种方法来获取两个路径,然后判断哪一个路径是不为null,并且带有图片格式后缀的路径,就拿来用。
	这里的用,是压缩图片后,再将压缩图片读进内存来。以下是我的方法:
</pre>


/**
     * 根据图片路径,得到压缩过的位图
     *
     * @param path
     * @param width
     * @param height
     * @return
     */
    public Bitmap getPressedBitmap(String path, int width, int height) {
        BitmapFactory.Options options = new BitmapFactory.Options();//new一个options
        options.inJustDecodeBounds = true;//先设置为true,即不读入图片到内存,先获取图片的信息,比如长宽等信息
        Bitmap bitmap = BitmapFactory.decodeFile(path, options);//此句代码是真正的去读取图片的长宽等信息,并且存储在options里面,在后面的代码中我们可以看到options.outWidth 和 options.outHeight得到的是图片的宽和高。这句代码所以不能没有,否则无法压缩图片。这里得到的bitmap是为null
        options.inSampleSize = getBitmapSampleSize(options, width, height)
;//根据给定的宽高来压缩图片的比例        options.inJustDecodeBounds = false;//设置为false,是要将图片以一定比例压缩后读入内存中        
<span style="white-space:pre">	</span>Bitmap bitmap1 = BitmapFactory.decodeFile(path, options);//
这里得到的bitmap才是不为null
        return bitmap1;}    
<pre name="code" class="java">/**
     * 根据要去的宽高,压缩图片
     *
     * @param options
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    public int getBitmapSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int imgWidth = options.outWidth;
        int imgHeight = options.outHeight;
        int inSimpleSize = 1;
        if (imgWidth > imgHeight || imgWidth < imgHeight) {
            final int heightRatio = imgWidth / reqWidth;
            final int widthRatio = imgHeight / reqHeight;
            inSimpleSize = widthRatio < heightRatio ? widthRatio : heightRatio;
        }
        return inSimpleSize;
    }


最后,我将图片选取好,上传到后台,发现图片的像素很低,几乎看不清上传的是什么,这个也是不符合需求的。所以要修改。
	我发现把options.inSampleSize = 5;//getBitmapSampleSize(options, width, height)改成这样子,inSampleSize改为你觉得可以看清楚的int就OK了。(这里提醒一下下,也要注意读取进来的bitmap的大小,图片也不能太大,因为上传是耗费流量的,我们一定要站在用户的角度去想问题)
	最后,问题实际上解决了,但是理论上还没有,想不通,怎么会我也没有时间去一一对看源码。等找个空时间,再去扒源码。
	
	对于第二个问题:利用拍照返回的照片是经过压缩的,分辨率很低,压根看不清楚。我们用显式intent来打开系统摄像头,然后回传图片信息,代码如下:	
bundle = data.getExtras();
Bitmap bitmap = (Bitmap) bundle.get("data");// 获取相机返回的数据,并转换为Bitmap图片格式
可以看到,调用系统相机拍照所返回的图片数据,是放到bundle里面的。我将bitmap拿出来,发现像素也是低到不行,根本没办法看,原来返回的是压缩得不行不行的图片。这当然也不行了。发现拍照的图片也没有保存在本地,不能重复使用那张图片。
	既然这样,我就查资料,想着系统应该会有方法设置可以返回原图的,但是我暂时没有找到。然后看到另外一个方法,先把拍照的照片存在指定的位置,然后利用指定位置去获取原图,压缩后,在读取进来。
	一开始在启动调用系统相机的时候,指定照片存储的位置。

// 利用系统自带的相机应用:拍照
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File filePath = getTakePhotoPath();//得到图片文件存储的路径
Uri imgUri = null;
imgUri = Uri.fromFile(filePath);
cacheImgPath = filePath.getAbsolutePath();//将图片的路径保存起来
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);//把路径传给系统,系统会自动存储到你指定的路径下
getParent().startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE2);

/**
     * 返回一个存储拍照的路径
     *
     * @return path
     */
    private File getTakePhotoPath() {
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
        Date date = new Date(System.currentTimeMillis());
        String fileName = format.format(date);
        File file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);
        String imgPath = file.getAbsolutePath() + "/test/";
        File fileDcim = new File(imgPath);
        if (!fileDcim.exists()) {
            fileDcim.mkdirs();
        }
        File filePicPath = new File(fileDcim, "SBD_" + fileName + ".jpg");
        if (!filePicPath.exists()) {
            try {
                filePicPath.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String path = filePicPath.getAbsolutePath();
        if (path == null) {
            return null;
        } else {
            return filePicPath;
        }
    }
在onactivityresult()回调中,利用保存起来的的路径,像上面一样获取本地图片的压缩图就OK了。
	当然,在做以上操作时,千万别忘了要添加相应的权限。
	以上就是我个人对这个问题的看法和解决方法,如果发现有何不对,或者有更好的方法解决问题,请赐教!


  • 2
    点赞
  • 0
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

姚镜堂

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值