Android开发:选择文件后如何获取含扩展名的真实完整文件路径
解决方案
问题根因
Android 4.4及以上版本中,ACTION_GET_CONTENT返回的默认是content://类型的URI,而非file://类型,直接调用Uri.getPath()获取到的是媒体库的索引标识,不是文件的真实物理路径。
实现步骤
1. 声明必要权限
在AndroidManifest.xml中添加存储读取权限,适配不同系统版本:
<!-- Android 12及以下版本所需权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Android 13及以上版本,仅需访问图片时申请该权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
2. 添加URI转真实路径工具方法
针对不同系统版本提供两种兼容方案:
方案一:直接查询媒体库获取物理路径(适用于Android 9及以下,或已适配分区存储的场景)
public String getRealPathFromURI(Context context, Uri contentUri) { String[] projection = { MediaStore.Images.Media.DATA }; Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null); if (cursor == null) return null; int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); String path = cursor.getString(columnIndex); cursor.close(); return path; }
方案二:分区存储兼容方案(全版本适用,兼容性更高)
Android 10及以上开启分区存储后,直接查询媒体库路径可能返回空,可将选中文件拷贝到应用专属缓存目录后获取路径使用:
// 从URI获取文件名 private String getFileNameFromUri(Context context, Uri uri) { String fileName = null; Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); if (cursor != null && cursor.moveToFirst()) { fileName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); cursor.close(); } return fileName != null ? fileName : "temp_file"; } // 将URI对应文件拷贝到应用缓存目录,返回缓存文件的完整路径 public String getCacheFilePathFromUri(Context context, Uri uri) throws IOException { File cacheFile = new File(context.getCacheDir(), getFileNameFromUri(context, uri)); try (InputStream inputStream = context.getContentResolver().openInputStream(uri); FileOutputStream outputStream = new FileOutputStream(cacheFile)) { byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } } return cacheFile.getAbsolutePath(); }
3. 修改onActivityResult逻辑
替换原有直接取路径的代码,调用上述工具方法:
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 10 && resultCode == RESULT_OK && data != null) { TextView filename = findViewById(R.id.filenameTextView); Uri uri = data.getData(); // 根据需求选择使用其中一种方案 // String realPath = getRealPathFromURI(this, uri); String realPath = null; try { realPath = getCacheFilePathFromUri(this, uri); } catch (IOException e) { e.printStackTrace(); } if (realPath != null) { directory = realPath; filename.setText(directory); } } }
补充说明
如果仅需要展示文件名,不需要获取完整物理路径,可直接调用getFileNameFromUri方法获取带后缀的文件名,无需申请存储权限,兼容性最优。
内容的提问来源于stack exchange,提问作者C18
相关产品推荐
相关产品推荐

