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

1. 从相册获取图片

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

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

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

 @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case Constants.PHOTO_REQUEST_GALLERY:
Uri uri = data.getData();
break;
}
}
}

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

2.根据Uri获取path路径

2.1 Android 4.4以下获取图片路径

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

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

 /**
* @param context 上下文对象
* @param uri 当前相册照片的Uri
* @return 解析后的Uri对应的String
*/
@SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
String pathHead = "file:///";
// 1. DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// 1.1 ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[];
if ("primary".equalsIgnoreCase(type)) {
return pathHead + Environment.getExternalStorageDirectory() + "/" + split[];
}
}
// 1.2 DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.
withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return pathHead + getDataColumn(context,
contentUri, null, null);
}
// 1.3 MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[]; Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
} final String selection = "_id=?";
final String[] selectionArgs = new String[]{split[]}; return pathHead + getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// 2. MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
if (isGooglePhotosUri(uri)) {//判断是否是google相册图片
return uri.getLastPathSegment();
} else if (isGooglePlayPhotosUri(uri)) {//判断是否是Google相册图片
return getImageUrlWithAuthority(context, uri);
} else {//其他类似于media这样的图片,和android4.4以下获取图片path方法类似
return getFilePath_below19(context, uri);
}
}
// 3. 判断是否是文件形式 File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return pathHead + uri.getPath();
}
return null;
}
/**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
private static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
} /**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
private static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
} /**
* @param uri
* The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
private static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* 判断是否是Google相册的图片,类似于content://com.google.android.apps.photos.content/...
**/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
} /**
* 判断是否是Google相册的图片,类似于content://com.google.android.apps.photos.contentprovider/0/1/mediakey:/local%3A821abd2f-9f8c-4931-bbe9-a975d1f5fabc/ORIGINAL/NONE/1075342619
**/
public static boolean isGooglePlayPhotosUri(Uri uri) {
return "com.google.android.apps.photos.contentprovider".equals(uri.getAuthority());
}

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,我们可以通过下面的形式获取:

 /**
* Google相册图片获取路径
**/
public static String getImageUrlWithAuthority(Context context, Uri uri) {
InputStream is = null;
if (uri.getAuthority() != null) {
try {
is = context.getContentResolver().openInputStream(uri);
Bitmap bmp = BitmapFactory.decodeStream(is);
return writeToTempImageAndGetPathUri(context, bmp).toString();
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
/**
* 将图片流读取出来保存到手机本地相册中
**/
public static Uri writeToTempImageAndGetPathUri(Context inContext, Bitmap inImage) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
inImage.compress(Bitmap.CompressFormat.JPEG, , bytes);
String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
return Uri.parse(path);
}

通过流的形式将图片读进来,转成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在应用间共享文件吧 
具体裁剪图片代码如下

 /**
* @param activity 当前activity
* @param orgUri 剪裁原图的Uri
* @param desUri 剪裁后的图片的Uri
* @param aspectX X方向的比例
* @param aspectY Y方向的比例
* @param width 剪裁图片的宽度
* @param height 剪裁图片高度
* @param requestCode 剪裁图片的请求码
*/
public static void cropImageUri(Activity activity, Uri orgUri,
Uri desUri, int aspectX, int aspectY,
int width, int height, int requestCode) {
Intent intent = new Intent("com.android.camera.action.CROP");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
orgUri = FileProvider.getUriForFile(activity, "com.smilexie.storytree.fileprovider", new File(newUri.getPath()));
}
intent.setDataAndType(orgUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("outputX", width);
intent.putExtra("outputY", height);
intent.putExtra("scale", true);
//将剪切的图片保存到目标Uri中
intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri);
intent.putExtra("return-data", false);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
activity.startActivityForResult(intent, requestCode);
}

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

总结

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

Android根据图片Uri获取图片path绝对路径的几种方法【转】的更多相关文章

  1. iOS 获取文件的目录路径的几种方法 [转]

    iOS 获取文件的目录路径的几种方法 2 years ago davidzhang iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么. d ...

  2. C#根据字体名通过注册表获取该字体文件路径(win10)两种方法推荐第二种

    方法一: 直接先上源码: private System.Collections.Generic.SortedDictionary<string, string> ReadFontInfor ...

  3. Android相机、相册获取图片显示并保存到SD卡

    Android相机.相册获取图片显示并保存到SD卡 [复制链接]   电梯直达 楼主    发表于 2013-3-13 19:51:43 | 只看该作者 |只看大图  本帖最后由 happy小妖同学 ...

  4. 根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换

    转:http://blog.csdn.net/q445697127/article/details/40537945 /** * 根据Uri获取图片绝对路径,解决Android4.4以上版本Uri转换 ...

  5. android通过BitmapFactory.decodeFile获取图片bitmap报内存溢出的解决办法

    android通过BitmapFactory.decodeFile获取图片bitmap报内存溢出的解决办法 原方法: public static Bitmap getSmallBitmap(Strin ...

  6. 根据图片Uri获得图片文件

    2013-12-17 1. 根据联系人图片Uri获得图片文件并将它显示在ImageView上, 代码如下: Uri uri = Uri.parse("content://com.androi ...

  7. Qt 打开安卓相冊选择图片并获取图片的本地路径

    Qt 打开安卓相冊选择图片并获取图片的本地路径 过程例如以下: 通过 Intent 打开安卓的系统相冊. 推荐使用 QAndroidJniObject::getStaticObjectField 获取 ...

  8. 根据Uri获取文件的绝对路径

    简易版处理(实际并没发现有什么问题) public static String getRealPathFromURI(Context context, Uri contentURI) { String ...

  9. 【转】c# Image获得图片路径的三种方法 winform

    代码如下:c# pictureBox1.Image的获得图片路径的三种方法 winform 1.绝对路径:this.pictureBox2.Image=Image.FromFile("D:\ ...

随机推荐

  1. HttpRunner框架(一)

    HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架,只需编写维护一份 YAML/JSON 脚本,即可实现自动化测试.性能测试.线上监控.持续集成等多种测试需求. 中文使用文档地址:h ...

  2. celery异步认知

    celery是异步任务的框架 是由python实现的异步框架. 在使用celery我们经常分为三个部分, 第一部分就是我们所说的客户端, 就是发起异步任务的一方, 第二部分 任务队列 broker 第 ...

  3. CSS基础和选择器

    什么是CSS? CSS是指层叠样式表(Cascading Style Sheets),样式定义如何显示HTML元素,样式通常又会存在于样式表中.也就是说把HTML元素的样式都统一收集起来写在一个地方或 ...

  4. Python内置类型(5)--迭代器类型

    指能够被内置函数next调用并不断返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值的对象称为迭代器(Iterator) 其实以上的说法只是侠义上的迭代器的定义,在pyt ...

  5. odoo开发笔记 -- 后台日志输出及分析

    odoo开发笔记 -- 后台日志输出及分析 附:日志分析软件

  6. (转)Linux开启路由转发功能

    原文:https://www.linuxidc.com/Linux/2016-12/138661.htm 标记一下,今天想让一台Red Hat Enterprise Linux 7开通iptables ...

  7. SQL Server性能优化(14)索引碎片

    一. 外部碎片和内部碎片的概念 碎片的概念和检测,参考MSDN:https://msdn.microsoft.com/zh-cn/library/ms189858.aspx 二.对于碎片的解决办法 解 ...

  8. Python 解析har 文件将域名分类导出

    前言 作为程序员平时主要是使用 shadowsocks 作为代理工具的.shadowsocks 有个很明显的优点儿就是可以设置白名单和黑名单.白名单是会走shadowsocks的自动代理模式. 遇到的 ...

  9. Vue + Element UI 实现权限管理系统 前端篇(十五):嵌套外部网页

    嵌套外部网页 在有些时候,我们需要在我们的内容栏主区域显示外部网页.如查看服务端提供的SQL监控页面,接口文档页面等. 这个时候就要求我们的导航菜单能够解析嵌套网页的URL,并根据URL路由到相应的嵌 ...

  10. 火热的线上APP的源码分享,开箱即用

    这篇文章是写给iOS的程序员或产品经理的,同样,对于入门学习iOS开发的人,也是一个很好的实战演练,因为这里分享的是一个已经上架的.拿了源码就能正常运行起来的项目. 在介绍这个项目的源码分享之前,小编 ...