从URI获取本地路径path

本文介绍了一种在Android开发中将Uri转换为本地路径的方法。该方法能够处理两种类型的Uri:一种是通过查询媒体数据库获得路径;另一种是直接从文件型Uri获取路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 记录一下Android开发中 Uri转path的方法

/**
 * 从URI获取本地路径
 *
 * @param
 * @return
 */
public static  String getAbsoluteImagePath(Activity activity,Uri contentUri) {

    //如果是对媒体文件,在android开机的时候回去扫描,然后把路径添加到数据库中。
    //由打印的contentUri可以看到:2种结构。正常的是:content://那么这种就要去数据库读取path。
    //另外一种是Uri是 file:///那么这种是 Uri.fromFile(File file);得到的
    System.out.println(contentUri);

    String[] projection = { MediaStore.Images.Media.DATA };
    String urlpath;
    CursorLoader loader = new CursorLoader(activity,contentUri, projection, null, null, null);
    Cursor cursor = loader.loadInBackground();
    try
    {
        int column_index = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        urlpath =cursor.getString(column_index);
        //如果是正常的查询到数据库。然后返回结构
        return urlpath;
    }
    catch (Exception e)
    {

        e.printStackTrace();
        // TODO: handle exception
    }finally{
        if(cursor != null){
            cursor.close();
        }
    }
    //如果是文件。Uri.fromFile(File file)生成的uri。那么下面这个方法可以得到结果
    urlpath = contentUri.getPath();
    return urlpath;
}

 

<think>我们被要求实现一个Android功能:通过Intent选择本地文件并获取文件全路径。根据Android开发规范,通常使用Intent.ACTION_GET_CONTENT或Intent.ACTION_OPEN_DOCUMENT来让用户选择文件。然后,在onActivityResult中处理返回的数据,获取文件的Uri,并尝试获取文件的真实路径(全路径)。 但是,需要注意的是,从Android 7.0(API 24)开始,直接使用文件路径file://)被限制,推荐使用Content URI并通过ContentResolver来访问文件。因此,获取路径可能不是最佳实践,但有时仍然需要(比如使用一些需要绝对路径的库)。我们可以通过ContentResolver查询或者使用FileUtils类将Uri转换为文件路径。 步骤: 1. 启动Intent选择文件(例如,选择任意类型的文件,或者指定类型)。 2. 在onActivityResult中获取返回的Uri。 3.Uri转换为文件路径(全路径)。注意:由于不同设备、不同文件来源(如不同文件提供者)的差异,转换可能复杂,且不一定总是能获取到物理路径。因此,我们通常建议直接使用ContentResolver打开输入流来读取文件内容,而不是获取路径。但如果确实需要路径,我们可以尝试通过一些方法转换。 参考引用[1]中展示了从相机获取图片并保存到文件,然后使用文件路径。但这里我们是从文件选择器获取。 参考引用[4]展示了两个Activity之间通过Intent传值,其中使用了Bundle。但这里我们是从文件选择器返回的Intent中获取数据。 实现: 1. 启动文件选择Intent: ```java Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); // 选择所有类型的文件 intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent, REQUEST_CODE_FILE_PICK); // REQUEST_CODE_FILE_PICK是一个自定义的请求码 ``` 2. 在Activity或Fragment的onActivityResult方法中处理结果: ```java @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_FILE_PICK && resultCode == RESULT_OK) { if (data != null) { Uri uri = data.getData(); String filePath = getPathFromUri(uri); // 将Uri转换为文件路径 // 使用文件路径 } } } ``` 3. 实现getPathFromUri方法:这个方法需要处理不同的Uri来源(如content://, file://等)。我们可以使用以下方法(注意:对于某些Uri,可能无法获取路径,尤其是来自云存储的文件)。 ```java private String getPathFromUri(Uri uri) { String filePath = null; if (uri != null) { if ("content".equalsIgnoreCase(uri.getScheme())) { // 使用ContentResolver查询实际路径 Cursor cursor = null; try { cursor = getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null); if (cursor != null && cursor.moveToFirst()) { int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); filePath = cursor.getString(columnIndex); } } catch (Exception e) { e.printStackTrace(); } finally { if (cursor != null) { cursor.close(); } } // 如果上述方法没有获取到,可以尝试使用以下方法(针对不同Uri方案) if (filePath == null) { try { // 使用InputStream来读取文件,并保存到缓存文件,然后返回缓存文件的路径(如果只需要文件内容,可以直接读取流,但这里需要路径) // 不过,这种方法会复制文件,可能会影响性能 InputStream inputStream = getContentResolver().openInputStream(uri); File file = createFileFromInputStream(inputStream); if (file != null) { filePath = file.getAbsolutePath(); } } catch (Exception e) { e.printStackTrace(); } } } else if ("file".equalsIgnoreCase(uri.getScheme())) { filePath = uri.getPath(); } } return filePath; } private File createFileFromInputStream(InputStream inputStream) { try { File file = File.createTempFile("temp", null, getCacheDir()); OutputStream outputStream = new FileOutputStream(file); byte[] buffer = new byte[4 * 1024]; int read; while ((read = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, read); } outputStream.flush(); outputStream.close(); return file; } catch (IOException e) { e.printStackTrace(); } return null; } ``` 但是,上述方法对于某些Content Uri(如来自Google相册的)可能无法通过MediaStore获取路径。因此,更通用的方法是使用DocumentFile或者直接使用Uri打开输入流,然后创建临时文件(如上面代码中所示)。但请注意,创建临时文件意味着复制文件,对于大文件可能不理想。 另外,从Android 10(API 29)开始,对存储访问有了更严格的限制,推荐使用存储访问框架(SAF)和Content URI,而不是直接路径。 因此,如果可能,尽量避免使用文件路径,而是直接使用Uri和ContentResolver来操作文件。如果必须使用路径,可以考虑使用第三方库如android-FilePicker等,或者使用以下更全面的方法(但依然不能保证所有情况): 我们可以使用一个工具类来获取路径,例如: ```java public class FileUtils { @SuppressLint("NewApi") public static String getPath(Context context, Uri uri) { // DocumentProvider if (DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // 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 getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; 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[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } private static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } private static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } private static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } } ``` 然后,在onActivityResult中: ```java String filePath = FileUtils.getPath(this, uri); ``` 注意:这个工具类在大多数情况下有效,但并不是100%可靠,特别是对于第三方文件提供者(如Google Drive、Dropbox等)可能无法获取路径。对于这些情况,我们只能通过打开输入流来读取文件内容。 因此,根据需求,如果只是需要读取文件内容,建议直接使用ContentResolver打开InputStream,而不是获取路径。如果必须获取路径(例如使用需要文件路径的第三方库),则使用上述工具类,并处理可能为null的情况。 另外,从Android 11(API 30)开始,作用域存储(Scoped Storage)进一步实施,获取其他应用文件的路径变得更加困难。因此,建议使用Uri来访问文件内容。 总结步骤: 1. 启动文件选择Intent。 2. 在onActivityResult中获取Uri。 3. 使用工具类尝试获取路径,如果获取不到,则考虑使用输入流读取文件内容,或者保存为临时文件再使用临时文件的路径。 根据引用[3]和[4],我们看到通过Intent传值通常使用Bundle,但这里我们是从文件选择器返回的Intent中获取Uri,而不是在两个Activity之间传值。 因此,我们实现一个文件选择并获取路径的功能,注意处理不同Android版本的兼容性。 最后,我们生成相关问题。</think>在 Android 中通过 Intent 选择本地文件并获取文件全路径的实现步骤如下: ### 1. 启动文件选择器 Intent 使用 `Intent.ACTION_GET_CONTENT` 或 `Intent.ACTION_OPEN_DOCUMENT` 启动文件选择器: ```java // 启动文件选择器 private void openFilePicker() { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); // 选择所有文件类型 intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent, FILE_PICK_REQUEST_CODE); } ``` ### 2. 在 onActivityResult 中处理结果 ```java @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == FILE_PICK_REQUEST_CODE && resultCode == RESULT_OK) { if (data != null) { Uri uri = data.getData(); String filePath = getRealPathFromUri(uri); // 关键步骤 Log.d("File Path", filePath); // 输出文件全路径 } } } ``` ### 3. 关键方法:从 Uri 获取真实路径 ```java private String getRealPathFromUri(Uri uri) { String path = null; if (DocumentsContract.isDocumentUri(this, uri)) { // 处理 Document Provider 类型的 Uri String docId = DocumentsContract.getDocumentId(uri); if ("com.android.externalstorage.documents".equals(uri.getAuthority())) { // 外部存储 String[] split = docId.split(":"); String type = split[0]; if ("primary".equalsIgnoreCase(type)) { path = Environment.getExternalStorageDirectory() + "/" + split[1]; } } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { // 下载目录 Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.parseLong(docId) ); path = getDataColumn(this, contentUri, null, null); } else if ("com.android.providers.media.documents".equals(uri.getAuthority())) { // 媒体目录 String[] split = docId.split(":"); Uri contentUri = MediaStore.Files.getContentUri("external"); String selection = "_id=?"; String[] selectionArgs = new String[]{split[1]}; path = getDataColumn(this, contentUri, selection, selectionArgs); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { // 普通 ContentProvider path = getDataColumn(this, uri, null, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { // 直接文件路径 path = uri.getPath(); } return path; } // 辅助方法:从 ContentResolver 查询真实路径 private String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = MediaStore.Files.FileColumns.DATA; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { int column_index = cursor.getColumnIndexOrThrow(column); return cursor.getString(column_index); } } finally { if (cursor != null) cursor.close(); } return null; } ``` ### 注意事项 1. **权限声明**:在 `AndroidManifest.xml` 中添加存储权限: ```xml <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> ``` 2. **Android 11+ 适配**: - 使用 `Intent.ACTION_OPEN_DOCUMENT` 代替 `ACTION_GET_CONTENT` 获得持久访问权限 - 添加 `MANAGE_EXTERNAL_STORAGE` 权限(需 Google Play 特批) 3. **云文件处理**:对于 Google Drive 等云存储文件,可能无法获取物理路径,建议直接使用 Uri 和 `ContentResolver` 读取文件流[^1][^3] ### 备选方案(直接使用 Uri) 如果不需要物理路径,可直接通过 Uri 读取文件内容: ```java try (InputStream in = getContentResolver().openInputStream(uri)) { // 直接处理文件流 } catch (IOException e) { e.printStackTrace(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值