在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. Java学习笔记47(JDBC、SQL注入攻击原理以及解决)

    JDBC:java的数据库连接 JDBC本质是一套API,由开发公司定义的类和接口 这里使用mysql驱动,是一套类库,实现了接口 驱动程序类库,实现接口重写方法,由驱动程序操作数据库 JDBC操作步 ...

  2. 机器学习基石笔记:12 Nonlinear Transformation

    一.二次假设 实际上线性假设的模型复杂度是受到限制的, 需要高次假设打破这个限制. 假设数据不是线性可分的,但是可以被一个圆心在原点的圆分开, 需要我们重新设计基于该圆的PLA等算法吗? 不用, 只需 ...

  3. Python基础教程(第3版) 笔记(一)

    1.1 数和表达式: 除法运算的结果为小数,即浮点数 >>>1/2 0.5 除法运算为整数,使用双斜杠 >>>1//2 0 >>>5.0//2.4 ...

  4. jquery-validae

    $(document).ready(function() { jQuery.validator.addMethod("realName", function(value, elem ...

  5. C# 本进程执行完毕后再执行下一线程

    最近做了一套MES集成系统,由上料到成品使自动化运行,其中生产过程是逐步的,但是每一个动作都需要独立的线程进行数据监听,那么就需要实现线程等待. 代码: using System; using Sys ...

  6. js按钮 防重复提交

    给html 按钮加id属性   例: <button id="addBtn"  onclinck="check()">  </button&g ...

  7. Linux学习笔记之六————Linux常用命令之系统管理

    <1>查看当前日历:cal cal命令用于查看当前日历,-y显示整年日历: <2>显示或设置时间:date 设置时间格式(需要管理员权限): date [MMDDhhmm[[C ...

  8. SQL Server性能优化(11)非聚集索引的覆盖索引存储结构

    一,非聚集索引的include 非聚集索引的Include属性可以让非聚集索引包含其他列.如 CREATE NONCLUSTERED INDEX [NonIxUser] ON [dbo].[Users ...

  9. .NET 线程池编程技术

    摘要 深度探索 Microsoft .NET提供的线程池, 揭示什么情况下你需要用线程池以及 .NET框架下的线程池是如何实现的,并告诉你如何去使用线程池. 内容 介绍 .NET中的线程池 线程池中执 ...

  10. git在工作中的用法总结-环境安装篇

    使用git有很长一段时间了 ,平时用到的时候都是直接google,经常用到的一些也记录在笔记中,但有时候笔记太多,实在是太乱了(其实是我太懒~ 哈?),找都要半天的时候还不如直接google,今天有空 ...