• Android根据图片Uri获取图片path绝对路径的几种方法【转】


    在Android 编程中经常会用到Uri转化为文件路径,如我们从相册选择图片上传至服务器,一般上传前需要对图片进行压缩,这时候就要用到图片的绝对路径。 
    下面对我开发中uri转path路径遇到的问题进行总结,其中涉及到Android不同api下对于uri的处理,还有对于Google相册图片该如何获取其图片路径。

    1. 从相册获取图片

    我们从相册获取的图片的代码如下:

    1 // 激活系统图库,选择一张图片
    2 Intent intent = new Intent(Intent.ACTION_PICK);
    3 intent.setType("image/*");
    4 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    5 startActivityForResult(intent, Constants.PHOTO_REQUEST_GALLERY);

    当然针对Android 6.0以上系统还需要获取WRITE_EXTERNAL_STORAGE和READ_EXTERNAL_STORAGE操作手机sd卡权限才行。 
    然后再在onActivityResult中获取到图片的uri路径:

     1 @Override
     2 public void onActivityResult(int requestCode, int resultCode, Intent data) {
     3         super.onActivityResult(requestCode, resultCode, data);
     4         if (resultCode == Activity.RESULT_OK) {
     5             switch (requestCode) {
     6                 case Constants.PHOTO_REQUEST_GALLERY:
     7                     Uri uri = data.getData();
     8                     break;
     9              }
    10         }
    11     }

    Uri:通用资源标志符(Universal Resource Identifier, 简称”URI”),Uri代表要操作的数据,Android上可用的每种资源(如图像、视频片段等)都可以用Uri来表示。 
    Uri一般由三部分组成:访问资源的命名机制。存放资源的主机名。资源自身的名称,由路径表示。

    2.根据Uri获取path路径

    2.1 Android 4.4以下获取图片路径

     1 /**
     2      * 获取小于api19时获取相册中图片真正的uri
     3      * 对于路径是:content://media/external/images/media/33517这种的,需要转成/storage/emulated/0/DCIM/Camera/IMG_20160807_133403.jpg路径,也是使用这种方法
     4      * @param context
     5      * @param uri
     6      * @return
     7      */
     8     public static String getFilePath_below19(Context context,Uri uri) {
     9         //这里开始的第二部分,获取图片的路径:低版本的是没问题的,但是sdk>19会获取不到
    10         Cursor cursor = null;
    11         String path = "";
    12         try {
    13             String[] proj = {MediaStore.Images.Media.DATA};
    14             //好像是android多媒体数据库的封装接口,具体的看Android文档
    15             cursor = context.getContentResolver().query(uri, proj, null, null, null);
    16             //获得用户选择的图片的索引值
    17             int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
    18             //将光标移至开头 ,这个很重要,不小心很容易引起越界
    19             cursor.moveToFirst();
    20             //最后根据索引值获取图片路径   结果类似:/mnt/sdcard/DCIM/Camera/IMG_20151124_013332.jpg
    21             path = cursor.getString(column_index);
    22         } finally {
    23             if (cursor != null) {
    24                 cursor.close();
    25             }
    26         }
    27         return path;
    28     }

    2.2.2 对于Android 4.4及以上机型获取path

      1 /**
      2      * @param context 上下文对象
      3      * @param uri     当前相册照片的Uri
      4      * @return 解析后的Uri对应的String
      5      */
      6     @SuppressLint("NewApi")
      7     public static String getPath(final Context context, final Uri uri) {
      8 
      9         final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
     10         String pathHead = "file:///";
     11         // 1. DocumentProvider
     12         if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
     13             // 1.1 ExternalStorageProvider
     14             if (isExternalStorageDocument(uri)) {
     15                 final String docId = DocumentsContract.getDocumentId(uri);
     16                 final String[] split = docId.split(":");
     17                 final String type = split[0];
     18                 if ("primary".equalsIgnoreCase(type)) {
     19                     return pathHead + Environment.getExternalStorageDirectory() + "/" + split[1];
     20                 }
     21             }
     22             // 1.2 DownloadsProvider
     23             else if (isDownloadsDocument(uri)) {
     24                 final String id = DocumentsContract.getDocumentId(uri);
     25                 final Uri contentUri = ContentUris.
     26                         withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
     27                 return pathHead + getDataColumn(context,
     28                         contentUri, null, null);
     29             }
     30             // 1.3 MediaProvider
     31             else if (isMediaDocument(uri)) {
     32                 final String docId = DocumentsContract.getDocumentId(uri);
     33                 final String[] split = docId.split(":");
     34                 final String type = split[0];
     35 
     36                 Uri contentUri = null;
     37                 if ("image".equals(type)) {
     38                     contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
     39                 } else if ("video".equals(type)) {
     40                     contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
     41                 } else if ("audio".equals(type)) {
     42                     contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
     43                 }
     44 
     45                 final String selection = "_id=?";
     46                 final String[] selectionArgs = new String[]{split[1]};
     47 
     48                 return pathHead + getDataColumn(context, contentUri, selection, selectionArgs);
     49             }
     50         }
     51         // 2. MediaStore (and general)
     52         else if ("content".equalsIgnoreCase(uri.getScheme())) {
     53             if (isGooglePhotosUri(uri)) {//判断是否是google相册图片
     54                 return uri.getLastPathSegment();
     55             } else if (isGooglePlayPhotosUri(uri)) {//判断是否是Google相册图片
     56                 return getImageUrlWithAuthority(context, uri);
     57             } else {//其他类似于media这样的图片,和android4.4以下获取图片path方法类似
     58                 return getFilePath_below19(context, uri);
     59             }
     60         }
     61         // 3. 判断是否是文件形式 File
     62         else if ("file".equalsIgnoreCase(uri.getScheme())) {
     63             return pathHead + uri.getPath();
     64         }
     65         return null;
     66     } 
     67 /**
     68      * @param uri
     69      *         The Uri to check.
     70      * @return Whether the Uri authority is ExternalStorageProvider.
     71      */
     72     private static boolean isExternalStorageDocument(Uri uri) {
     73         return "com.android.externalstorage.documents".equals(uri.getAuthority());
     74     }
     75 
     76     /**
     77      * @param uri
     78      *         The Uri to check.
     79      * @return Whether the Uri authority is DownloadsProvider.
     80      */
     81     private static boolean isDownloadsDocument(Uri uri) {
     82         return "com.android.providers.downloads.documents".equals(uri.getAuthority());
     83     }
     84 
     85     /**
     86      * @param uri
     87      *         The Uri to check.
     88      * @return Whether the Uri authority is MediaProvider.
     89      */
     90     private static boolean isMediaDocument(Uri uri) {
     91         return "com.android.providers.media.documents".equals(uri.getAuthority());
     92     }
     93 /**
     94  *  判断是否是Google相册的图片,类似于content://com.google.android.apps.photos.content/...
     95 **/
     96 public static boolean isGooglePhotosUri(Uri uri) {
     97          return "com.google.android.apps.photos.content".equals(uri.getAuthority());
     98     }
     99 
    100 /**
    101  *  判断是否是Google相册的图片,类似于content://com.google.android.apps.photos.contentprovider/0/1/mediakey:/local%3A821abd2f-9f8c-4931-bbe9-a975d1f5fabc/ORIGINAL/NONE/1075342619
    102 **/
    103 public static boolean isGooglePlayPhotosUri(Uri uri) {
    104     return "com.google.android.apps.photos.contentprovider".equals(uri.getAuthority());
    105 } 

    2.2.3 针对Google相册图片获取方法

    从相册中选择图片,如果手机安装了Google Photo,它的路径格式如下: 
    content://com.google.android.apps.photos.contentprovider/0/1/mediakey%3A%2Flocal%253A821abd2f-9f8c-4931-bbe9-a975d1f5fabc/ORIGINAL/NONE/1754758324 
    用原来的方式获取是不起作用的,path会是null,我们可以通过下面的形式获取:

     1 /**
     2  * Google相册图片获取路径
     3 **/
     4 public static String getImageUrlWithAuthority(Context context, Uri uri) {
     5         InputStream is = null;
     6         if (uri.getAuthority() != null) {
     7             try {
     8                 is = context.getContentResolver().openInputStream(uri);
     9                 Bitmap bmp = BitmapFactory.decodeStream(is);
    10                 return writeToTempImageAndGetPathUri(context, bmp).toString();
    11             } catch (FileNotFoundException e) {
    12                 e.printStackTrace();
    13             }finally {
    14                 try {
    15                     is.close();
    16                 } catch (IOException e) {
    17                     e.printStackTrace();
    18                 }
    19             }
    20         }
    21         return null;
    22     }
    23 /**
    24  * 将图片流读取出来保存到手机本地相册中
    25 **/
    26 public static Uri writeToTempImageAndGetPathUri(Context inContext, Bitmap inImage) {
    27     ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    28     inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
    29     String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
    30     return Uri.parse(path);
    31 }   

    通过流的形式将图片读进来,转成bitmap形式,再写进手机媒体中,转换成路径如下:content://media/external/images/media/1754758324,因为Google中的图片并不在我们系统手机相册中,需要先下载,再转存。

    3. 针对Android 7.0及以上机型打开Uri适配

    Android7.0以上机型,若要在应用间共享文件,应发送一项 content:// URI,并授予 Uri临时访问权限。进行此授权的最简单方式是使用 FileProvider 类。如需了解有关权限和共享文件的详细信息,请参阅官方的共享文件

    具体需要在AndroidMainfest.xml文件中添加provider标签,并定义相应的路径,具体可参考鸿洋写的Android 7.0 行为变更 通过FileProvider在应用间共享文件吧 
    具体裁剪图片代码如下

     1 /**
     2      * @param activity  当前activity
     3      * @param orgUri 剪裁原图的Uri
     4      * @param desUri 剪裁后的图片的Uri
     5      * @param aspectX X方向的比例
     6      * @param aspectY  Y方向的比例
     7      * @param width 剪裁图片的宽度
     8      * @param height 剪裁图片高度
     9      * @param requestCode  剪裁图片的请求码
    10      */
    11     public static void cropImageUri(Activity activity, Uri orgUri,
    12                                     Uri desUri, int aspectX, int aspectY,
    13                                     int width, int height, int requestCode) {
    14         Intent intent = new Intent("com.android.camera.action.CROP");
    15         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    16             intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    17             orgUri = FileProvider.getUriForFile(activity, "com.smilexie.storytree.fileprovider", new File(newUri.getPath()));
    18         }
    19         intent.setDataAndType(orgUri, "image/*");
    20         intent.putExtra("crop", "true");
    21         intent.putExtra("outputX", width);
    22         intent.putExtra("outputY", height);
    23         intent.putExtra("scale", true);
    24         //将剪切的图片保存到目标Uri中
    25         intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri);
    26         intent.putExtra("return-data", false);
    27         intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
    28         intent.putExtra("noFaceDetection", true);
    29         activity.startActivityForResult(intent, requestCode);
    30     }

    只要是打开文件,uri都需要更改,不然解析异常,但是保存进uri则不需要修改。

    总结

    Android各版本,各机型适配不得不说有很多坑,比如我在开发中遇到google相册图片,老是获取不到路径。只能平时多关注Android新版本发布时,会对现有应用产生什么影响,遇到问题多查看别人的解决办法,多归纳,多总结,这样才能使咱们的应用越来越完善。

  • 相关阅读:
    目前主要的测试用例设计方法是什么?
    软件的安全性应从哪几个方面去测试?
    软件产品质量特性是什么?
    在您以往的工作中,一条软件缺陷(或者叫Bug)记录都包含了哪些内容?如何提交高质量的软件缺陷(Bug)记录?
    简述什么是静态测试、动态测试、黑盒测试、白盒测试、α测试 β测试
    详细的描述一个测试活动完整的过程
    在搜索引擎中输入汉字就可以解析到对应的域名,请问如何用LoadRunner进行测试。
    String是最基本的数据类型吗?
    1、面向对象的特征有哪些方面
    说出Servlet的生命周期,并说出Servlet和CGI的区别。
  • 原文地址:https://www.cnblogs.com/blog4wei/p/9578405.html
Copyright © 2020-2023  润新知