Android开发:无法从Google Drive及Download返回的Uri获取图片路径
嘿,我来帮你搞定这个Android图片Uri解析的难题!你当前的getRealPathFromURI方法只适配了MediaStore来源的Uri(也就是你说的第三种情况),但Google Drive和Downloads提供的Uri属于DocumentsProvider范畴,这类Uri的解析逻辑完全不同,直接查MediaStore的DATA列肯定拿不到结果,下面给你分情况拆解解决方案:
问题核心原因
Google Drive的Uri指向的可能是云端文件的本地缓存,甚至直接是云端数据流,根本没有真实的本地路径;而Downloads的Uri是通过DocumentsProvider封装的,需要解析DocumentId才能拿到真实路径。
分场景处理方案
1. 处理Downloads来源的Uri
Downloads的Uri格式是content://com.android.providers.downloads.documents/document/raw%3A...,这里的raw:前缀后面跟着的就是URL编码后的本地文件路径,我们可以这样解析:
- 先通过
DocumentsContract.getDocumentId(uri)拿到DocumentId - 如果Id以
raw:开头,直接截取后面的部分并做URL解码,就能得到真实路径
2. 处理Google Drive来源的Uri
Google Drive的Uri格式是content://com.google.android.apps.docs.storage.legacy/...,这类Uri没有对应的本地文件路径(或者路径是应用私有缓存,你无法直接访问),最可靠的方式是直接通过ContentResolver读取输入流,而不是强行获取路径。
3. 整合所有场景的通用工具方法
下面是整合了三种Uri情况的完整方法,同时兼容不同Android版本:
public static String getRealPathFromURI(Context context, Uri uri) { // 先判断是否是DocumentsProvider类型的Uri if (DocumentsContract.isDocumentUri(context, uri)) { String authority = uri.getAuthority(); // 处理Downloads的DocumentsProvider if ("com.android.providers.downloads.documents".equals(authority)) { String documentId = DocumentsContract.getDocumentId(uri); // 处理raw格式的路径 if (documentId.startsWith("raw:")) { return Uri.decode(documentId.substring(4)); } // 非raw格式,通过Downloads的ContentUri查询 String[] projection = {MediaStore.MediaColumns.DATA}; Cursor cursor = context.getContentResolver().query( ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(documentId)), projection, null, null, null); if (cursor != null && cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndex(projection[0]); String path = cursor.getString(columnIndex); cursor.close(); return path; } } // 处理MediaStore的DocumentsProvider(你的第三种情况) else if ("com.android.providers.media.documents".equals(authority)) { String wholeID = DocumentsContract.getDocumentId(uri); String id = wholeID.split(":")[1]; String[] column = {MediaStore.Images.Media.DATA}; String sel = MediaStore.Images.Media._ID + "=?"; Cursor cursor = context.getContentResolver().query( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column, sel, new String[]{id}, null); if (cursor != null && cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndex(column[0]); String filePath = cursor.getString(columnIndex); cursor.close(); return filePath; } } // Google Drive的Uri无法获取真实本地路径,返回null提示用流处理 else if ("com.google.android.apps.docs.storage.legacy".equals(authority)) { return null; } } // 非DocumentsProvider的普通Content Uri(比如直接的MediaStore Uri) else if ("content".equalsIgnoreCase(uri.getScheme())) { String[] filePathColumn = {MediaStore.Images.Media.DATA}; Cursor cursor = context.getContentResolver().query(uri, filePathColumn, null, null, null); if (cursor != null && cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String path = cursor.getString(columnIndex); cursor.close(); return path; } } // 文件类型的Uri(file:///开头) else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; }
更稳妥的内容获取方式:直接使用输入流
对于Google Drive这类没有本地路径的Uri,强行获取路径是行不通的,推荐直接通过ContentResolver打开输入流来读取图片内容,这种方式适配所有来源:
// 从Uri获取Bitmap public static Bitmap getBitmapFromUri(Context context, Uri uri) throws IOException { InputStream inputStream = context.getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); if (inputStream != null) { inputStream.close(); } return bitmap; } // 将Uri内容保存到本地文件 public static void saveUriContentToFile(Context context, Uri uri, File outputFile) throws IOException { try (InputStream inputStream = context.getContentResolver().openInputStream(uri); OutputStream outputStream = new FileOutputStream(outputFile)) { byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } } }
注意事项
- 权限问题:Android 6.0+需要动态申请
READ_EXTERNAL_STORAGE,但通过ACTION_GET_CONTENT选择的图片,系统已经授予了临时访问权限,无需额外申请。 - 避免硬编码包名:虽然我们处理了Google Drive和Downloads的Authority,但尽量不要依赖硬编码的包名,优先用
DocumentsContract.isDocumentUri()判断类型,适配未来的系统变化。 - 路径的不可靠性:越来越多的云端应用不再提供真实本地路径,所以尽量使用流处理内容,而不是依赖路径。
内容的提问来源于stack exchange,提问作者Nava

