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

<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了。

	当然,在做以上操作时,千万别忘了要添加相应的权限。
	以上就是我个人对这个问题的看法和解决方法,如果发现有何不对,或者有更好的方法解决问题,请赐教!
时间: 2024-10-10 16:40:04

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

android图片压缩上传系列-service篇

本篇文章是继续上篇android图片压缩上传系列-基础篇文章的续篇.主要目的是:通过Service来执行图片压缩任务来讨论如何使用Service,如何处理任务量大的并发问题. 了解下Service 大家都知道如果有费时任务,这时需要将任务放到后台线程中执行,如果对操作的结果需要通过ui展示还需要在任务完成后通知前台更新.当然对于这种情况,大家也可以在Activity中启动线程,在线程中通过Handler和sendMessage来通知Activity并执行更新ui的操作,但是更好的方法是将这些操作

HTML5 图片本地压缩上传插件

======================前端代码========================= <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>localResizeIMG</title> <!--引入JQuery 用于异步上传图片--> <script type="text/javascript"

CANVAS运用-对图片的压缩上传(仅针对移动浏览器)

最近在移动端设计头像上传功能时,原本是以<input type="file">直接通过formData上传,然而实际使用情况是:对于过大的图片(高像素手机所拍摄的照片等)上传时间过长会导致上传失败,而每次都上传原始大小的图片(后台处理压缩)十分影响用户体验,所以研究了一下通过canvas压缩图片并上传的方法,以下是整理的一些思路和心得: 一.<input type="file">获取本地图片,并将图片绘制到画布中.此处的难点在于:由于浏览器的

Html5+asp.net mvc 图片压缩上传

在做图片上传时,大图片如果没有压缩直接上传时间会非常长,因为有的图片太大,传到服务器上再压缩太慢了,而且损耗流量. 思路是将图片抽样显示在canvas上,然后用通过canvas.toDataURL方法得到base64字符串来实现压缩. 废话不多少不多说直接看代码: 本次测试使用了 zepto.min.js 插件,更新版本的下载请点击这里 主要js代码: //图片压缩处理 ; (function () { /** * 加载的时候进行抽样检测 * 在iOS中,大于2M的图片会抽样渲染 */ func

Android上传图片到PHP服务器并且支持浏览器上传文件(word、图片、音乐等)

暑假已经过了一半了,这才完成计划当中的第二个任务.虽然进度是慢了点.但也算是暑假的收获吧.下面我就把我学习当中的收获记录在此. 还是跟以往一样,先上图片. 操作的步骤:打开程序---->选择上传的照片----->点击返回键------>显示没有选择上传图片的toast------>点击上传的图片----->打印图片的存储的物理路径---->询问是否确认上传选择的图片----->确认则显示上传成功---->取消则退出 php代码: <?php //上传文

纯原生js移动端图片压缩上传插件

前段时间,同事又来咨询一个问题了,说手机端动不动拍照就好几M高清大图,上传服务器太慢,问问我有没有可以压缩图片并上传的js插件,当然手头上没有,别慌,我去网上搜一搜. 结果呢,呵呵...诶~又全是基于jquery.zepto的(这句话似乎吐槽次数太多了...),然后我也就不吐槽了, 然后当然是自己做了,先上图: 纯原生js的移动端图片压缩上传插件,不依赖任何库 用法 在html页面中引入input标签,通过自定义属性data-LUploader绑定点击触发的标签id,写法如下: <div cla

前端图片压缩上传(纯js的质量压缩,非大小压缩)

此demo为大于1M对图片进行压缩上传 若小于1M则原图上传,可以根据自己实际需求更改. demo源码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>XMLHttpRequest上传文件</title> <script type="text/javascript"> /* 三个参数 file:一个是文件(

基于H5+ API手机相册图片压缩上传

// 母函数 function App(){} /** * 图片压缩,默认同比例压缩 * @param {Object} path * pc端传入的路径可以为相对路径,但是在移动端上必须传入的路径是照相图片储存的绝对路径 * @param {Object} obj * obj 对象 有 width, height, quality(0-1) * @param {Object} callback * 回调函数有一个参数,base64的字符串数据 */ App.prototype.dealImage

springMVC多图片压缩上传的实现

前台代码: <form id="userForm" method="post" enctype="multipart/form-data"> <div> <label class="my_input_title">图片1:</label> <input type="file" name="file" class=""

前端图片压缩上传(纯js的质量压缩,非长宽压缩)

原文: 此demo为大于1M对图片进行压缩上传 若小于1M则原图上传,可以根据自己实际需求更改. demo源码如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>XMLHttpRequest上传文件</title> <script type="text/javascript"> /* 三个参数 file:一个