如何从 URI 获取完整的文件路径
Posted
技术标签:
【中文标题】如何从 URI 获取完整的文件路径【英文标题】:How to get the Full file path from URI 【发布时间】:2012-10-23 22:53:47 【问题描述】:我想从 URI 获取完整的文件路径。 URI 不是图像,而是音乐文件,但如果我像 MediaStore 解决方案那样做,如果应用程序用户选择例如 Astro 作为浏览器,而不是音乐播放器,它将不起作用。我该如何解决?
【问题讨论】:
【参考方案1】:PathUtil 方法只能在 oreo 下工作,如果它是 oreo 则可能会崩溃,因为在 oreo 中我们不会获取 id 而是 data.getData() 中的整个路径,所以你需要做的就是从 uri 创建一个文件并从 getPath() 获取其路径并拆分它。下面是工作代码:-
Uri uri = data.getData();
File file = new File(uri.getPath());//create path from uri
final String[] split = file.getPath().split(":");//split the path.
filePath = split[1];//assign it to a string(your choice).
上面的代码可以在 oreo 中运行,如果它低于 oreo 则 PathUtil 可以运行。谢谢!
String filePath=PathUtil.getPath(context,yourURI);
PathUtil.java
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import java.net.URISyntaxException;
/**
* Created by Aki on 1/7/2017.
*/
public class PathUtil
/*
* Gets the file path of the given Uri.
*/
@SuppressLint("NewApi")
public static String getPath(Context context, Uri uri) throws URISyntaxException
final boolean needToCheckUri = Build.VERSION.SDK_INT >= 19;
String selection = null;
String[] selectionArgs = null;
// Uri is different in versions after KITKAT (Android 4.4), we need to
// deal with different Uris.
if (needToCheckUri && DocumentsContract.isDocumentUri(context.getApplicationContext(), uri))
if (isExternalStorageDocument(uri))
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
return Environment.getExternalStorageDirectory() + "/" + split[1];
else if (isDownloadsDocument(uri))
final String id = DocumentsContract.getDocumentId(uri);
uri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
else if (isMediaDocument(uri))
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("image".equals(type))
uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
else if ("video".equals(type))
uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
else if ("audio".equals(type))
uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
selection = "_id=?";
selectionArgs = new String[] split[1] ;
if ("content".equalsIgnoreCase(uri.getScheme()))
String[] projection = MediaStore.Images.Media.DATA ;
Cursor cursor = null;
try
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (cursor.moveToFirst())
return cursor.getString(column_index);
catch (Exception e)
else if ("file".equalsIgnoreCase(uri.getScheme()))
return uri.getPath();
return null;
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public 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.
*/
public 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.
*/
public static boolean isMediaDocument(Uri uri)
return "com.android.providers.media.documents".equals(uri.getAuthority());
【讨论】:
仍然给我null。 @akhilesh0707 getPath() 将为外部 sdcard 文件返回 null。有什么解决方案吗?? 这是最全面的答案,它解释了我能够提出的每一种情况,目前应该是公认的答案 这不适用于 Android 9 上的下载文件:java.lang.IllegalArgumentException: Unknown URI: content://downloads/public_downloads/18 您能否从这些 URI “content://com.android.providers.media.documents/document/document%3A80”(文档)和“content://com.android.providers”中获取路径.downloads.documents/document/msf%3A81"(下载)?【参考方案2】:当前页面上存在的一个答案 (this) 是正确的,但有一些错误。例如,它不适用于 API 29+ 的设备。 我将更新上面的代码并发布它的新版本。 我认为这篇文章应该被标记为最终答案。
更新代码:(添加 WhatsApp 支持)
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class FileUtils
private static Uri contentUri = null;
Context context;
public FileUtils( Context context)
this.context=context;
@SuppressLint("NewApi")
public static String getPath( final Uri uri)
// check here to KITKAT or new version
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
String selection = null;
String[] selectionArgs = null;
// DocumentProvider
if (isKitKat )
// ExternalStorageProvider
if (isExternalStorageDocument(uri))
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
String fullPath = getPathFromExtSD(split);
if (fullPath != "")
return fullPath;
else
return null;
// DownloadsProvider
if (isDownloadsDocument(uri))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
final String id;
Cursor cursor = null;
try
cursor = context.getContentResolver().query(uri, new String[]MediaStore.MediaColumns.DISPLAY_NAME, null, null, null);
if (cursor != null && cursor.moveToFirst())
String fileName = cursor.getString(0);
String path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;
if (!TextUtils.isEmpty(path))
return path;
finally
if (cursor != null)
cursor.close();
id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id))
if (id.startsWith("raw:"))
return id.replaceFirst("raw:", "");
String[] contentUriPrefixesToTry = new String[]
"content://downloads/public_downloads",
"content://downloads/my_downloads"
;
for (String contentUriPrefix : contentUriPrefixesToTry)
try
final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
catch (NumberFormatException e)
//In Android 8 and Android P the id is not a number
return uri.getPath().replaceFirst("^/document/raw:", "").replaceFirst("^raw:", "");
else
final String id = DocumentsContract.getDocumentId(uri);
if (id.startsWith("raw:"))
return id.replaceFirst("raw:", "");
try
contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
catch (NumberFormatException e)
e.printStackTrace();
if (contentUri != null)
return getDataColumn(context, contentUri, null, null);
// MediaProvider
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;
selection = "_id=?";
selectionArgs = new String[]split[1];
return getDataColumn(context, contentUri, selection,
selectionArgs);
if (isGoogleDriveUri(uri))
return getDriveFilePath(uri);
if(isWhatsAppFile(uri))
return getFilePathForWhatsApp(uri);
if ("content".equalsIgnoreCase(uri.getScheme()))
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
if (isGoogleDriveUri(uri))
return getDriveFilePath(uri);
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
// return getFilePathFromURI(context,uri);
return copyFileToInternalStorage(uri,"userfiles");
// return getRealPathFromURI(context,uri);
else
return getDataColumn(context, uri, null, null);
if ("file".equalsIgnoreCase(uri.getScheme()))
return uri.getPath();
else
if(isWhatsAppFile(uri))
return getFilePathForWhatsApp(uri);
if ("content".equalsIgnoreCase(uri.getScheme()))
String[] projection =
MediaStore.Images.Media.DATA
;
Cursor cursor = null;
try
cursor = context.getContentResolver()
.query(uri, projection, selection, selectionArgs, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
if (cursor.moveToFirst())
return cursor.getString(column_index);
catch (Exception e)
e.printStackTrace();
return null;
private boolean fileExists(String filePath)
File file = new File(filePath);
return file.exists();
private String getPathFromExtSD(String[] pathData)
final String type = pathData[0];
final String relativePath = "/" + pathData[1];
String fullPath = "";
// on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string
// something like "71F8-2C0A", some kind of unique id per storage
// don't know any API that can get the root path of that storage based on its id.
//
// so no "primary" type, but let the check here for other devices
if ("primary".equalsIgnoreCase(type))
fullPath = Environment.getExternalStorageDirectory() + relativePath;
if (fileExists(fullPath))
return fullPath;
// Environment.isExternalStorageRemovable() is `true` for external and internal storage
// so we cannot relay on it.
//
// instead, for each possible path, check if file exists
// we'll start with secondary storage as this could be our (physically) removable sd card
fullPath = System.getenv("SECONDARY_STORAGE") + relativePath;
if (fileExists(fullPath))
return fullPath;
fullPath = System.getenv("EXTERNAL_STORAGE") + relativePath;
if (fileExists(fullPath))
return fullPath;
return fullPath;
private String getDriveFilePath(Uri uri)
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File file = new File(context.getCacheDir(), name);
try
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1)
outputStream.write(buffers, 0, read);
Log.e("File Size", "Size " + file.length());
inputStream.close();
outputStream.close();
Log.e("File Path", "Path " + file.getPath());
Log.e("File Size", "Size " + file.length());
catch (Exception e)
Log.e("Exception", e.getMessage());
return file.getPath();
/***
* Used for Android Q+
* @param uri
* @param newDirName if you want to create a directory, you can set this variable
* @return
*/
private String copyFileToInternalStorage(Uri uri,String newDirName)
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, new String[]
OpenableColumns.DISPLAY_NAME,OpenableColumns.SIZE
, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File output;
if(!newDirName.equals(""))
File dir = new File(context.getFilesDir() + "/" + newDirName);
if (!dir.exists())
dir.mkdir();
output = new File(context.getFilesDir() + "/" + newDirName + "/" + name);
else
output = new File(context.getFilesDir() + "/" + name);
try
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(output);
int read = 0;
int bufferSize = 1024;
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1)
outputStream.write(buffers, 0, read);
inputStream.close();
outputStream.close();
catch (Exception e)
Log.e("Exception", e.getMessage());
return output.getPath();
private String getFilePathForWhatsApp(Uri uri)
return copyFileToInternalStorage(uri,"whatsapp");
private 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 index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
finally
if (cursor != null)
cursor.close();
return null;
private boolean isExternalStorageDocument(Uri uri)
return "com.android.externalstorage.documents".equals(uri.getAuthority());
private boolean isDownloadsDocument(Uri uri)
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
private boolean isMediaDocument(Uri uri)
return "com.android.providers.media.documents".equals(uri.getAuthority());
private boolean isGooglePhotosUri(Uri uri)
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
public boolean isWhatsAppFile(Uri uri)
return "com.whatsapp.provider.media".equals(uri.getAuthority());
private boolean isGoogleDriveUri(Uri uri)
return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
【讨论】:
您尝试过使用新的谷歌照片应用吗?它不工作。 你是我的英雄 谢谢伙计。你是生命的救星。但我建议您将其保留在 Github 的公共要点中。这样我们就可以轻松地为关注更新添加书签 考虑将其作为库发布到 Github。我发现它是从 Android 4 到 10 最一致的 这个解决方案对我很有效,尤其是从 WhatsApp 等其他非图库应用接收图片时【参考方案3】:用途:
String path = yourAndroidURI.uri.getPath() // "/mnt/sdcard/FileName.mp3"
File file = new File(new URI(path));
或
String path = yourAndroidURI.uri.toString() // "file:///mnt/sdcard/FileName.mp3"
File file = new File(new URI(path));
【讨论】:
构造函数 File(URI) 未定义? 在我的机器上,Uri::getPath 的结果会以“/”开头,而 Uri::toString() 的结果会以“file://”开头。 这个答案是有害的,不值得它获得的选票。如果 Uri 是content://
或任何其他非文件 Uri,则会失败。
引起:java.lang.IllegalArgumentException:URI 不是绝对的
我不明白为什么这仍然是公认的答案。它现在根本行不通。【参考方案4】:
从 URI 获取路径 使用下面的类用于 android 所有版本。 访问任何类型的文件。
package com.satya.filemangerdemo.common;
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.List;
public class FileUtils
private static Uri contentUri = null;
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.<br>
* <br>
* Callers should check whether the path is local before assuming it
* represents a local file.
*
* @param context The context.
* @param uri The Uri to query.
*/
@SuppressLint("NewApi")
public static String getPath(final Context context, final Uri uri)
// check here to KITKAT or new version
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
String selection = null;
String[] selectionArgs = null;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri))
// ExternalStorageProvider
if (isExternalStorageDocument(uri))
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
String fullPath = getPathFromExtSD(split);
if (fullPath != "")
return fullPath;
else
return null;
// DownloadsProvider
else if (isDownloadsDocument(uri))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
final String id;
Cursor cursor = null;
try
cursor = context.getContentResolver().query(uri, new String[]MediaStore.MediaColumns.DISPLAY_NAME, null, null, null);
if (cursor != null && cursor.moveToFirst())
String fileName = cursor.getString(0);
String path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;
if (!TextUtils.isEmpty(path))
return path;
finally
if (cursor != null)
cursor.close();
id = DocumentsContract.getDocumentId(uri);
if (!TextUtils.isEmpty(id))
if (id.startsWith("raw:"))
return id.replaceFirst("raw:", "");
String[] contentUriPrefixesToTry = new String[]
"content://downloads/public_downloads",
"content://downloads/my_downloads"
;
for (String contentUriPrefix : contentUriPrefixesToTry)
try
final Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.valueOf(id));
/* final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));*/
return getDataColumn(context, contentUri, null, null);
catch (NumberFormatException e)
//In Android 8 and Android P the id is not a number
return uri.getPath().replaceFirst("^/document/raw:", "").replaceFirst("^raw:", "");
else
final String id = DocumentsContract.getDocumentId(uri);
final boolean isOreo = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
if (id.startsWith("raw:"))
return id.replaceFirst("raw:", "");
try
contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
catch (NumberFormatException e)
e.printStackTrace();
if (contentUri != null)
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;
selection = "_id=?";
selectionArgs = new String[]split[1];
return getDataColumn(context, contentUri, selection,
selectionArgs);
else if (isGoogleDriveUri(uri))
return getDriveFilePath(uri, context);
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme()))
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
if (isGoogleDriveUri(uri))
return getDriveFilePath(uri, context);
if( Build.VERSION.SDK_INT == Build.VERSION_CODES.N)
// return getFilePathFromURI(context,uri);
return getMediaFilePathForN(uri, context);
// return getRealPathFromURI(context,uri);
else
return getDataColumn(context, uri, null, null);
// File
else if ("file".equalsIgnoreCase(uri.getScheme()))
return uri.getPath();
return null;
/**
* Check if a file exists on device
*
* @param filePath The absolute file path
*/
private static boolean fileExists(String filePath)
File file = new File(filePath);
return file.exists();
/**
* Get full file path from external storage
*
* @param pathData The storage type and the relative path
*/
private static String getPathFromExtSD(String[] pathData)
final String type = pathData[0];
final String relativePath = "/" + pathData[1];
String fullPath = "";
// on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string
// something like "71F8-2C0A", some kind of unique id per storage
// don't know any API that can get the root path of that storage based on its id.
//
// so no "primary" type, but let the check here for other devices
if ("primary".equalsIgnoreCase(type))
fullPath = Environment.getExternalStorageDirectory() + relativePath;
if (fileExists(fullPath))
return fullPath;
// Environment.isExternalStorageRemovable() is `true` for external and internal storage
// so we cannot relay on it.
//
// instead, for each possible path, check if file exists
// we'll start with secondary storage as this could be our (physically) removable sd card
fullPath = System.getenv("SECONDARY_STORAGE") + relativePath;
if (fileExists(fullPath))
return fullPath;
fullPath = System.getenv("EXTERNAL_STORAGE") + relativePath;
if (fileExists(fullPath))
return fullPath;
return fullPath;
private static String getDriveFilePath(Uri uri, Context context)
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File file = new File(context.getCacheDir(), name);
try
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1)
outputStream.write(buffers, 0, read);
Log.e("File Size", "Size " + file.length());
inputStream.close();
outputStream.close();
Log.e("File Path", "Path " + file.getPath());
Log.e("File Size", "Size " + file.length());
catch (Exception e)
Log.e("Exception", e.getMessage());
return file.getPath();
private static String getMediaFilePathForN(Uri uri, Context context)
Uri returnUri = uri;
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
String name = (returnCursor.getString(nameIndex));
String size = (Long.toString(returnCursor.getLong(sizeIndex)));
File file = new File(context.getFilesDir(), name);
try
InputStream inputStream = context.getContentResolver().openInputStream(uri);
FileOutputStream outputStream = new FileOutputStream(file);
int read = 0;
int maxBufferSize = 1 * 1024 * 1024;
int bytesAvailable = inputStream.available();
//int bufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
final byte[] buffers = new byte[bufferSize];
while ((read = inputStream.read(buffers)) != -1)
outputStream.write(buffers, 0, read);
Log.e("File Size", "Size " + file.length());
inputStream.close();
outputStream.close();
Log.e("File Path", "Path " + file.getPath());
Log.e("File Size", "Size " + file.length());
catch (Exception e)
Log.e("Exception", e.getMessage());
return file.getPath();
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 index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
finally
if (cursor != null)
cursor.close();
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());
/**
* @param uri - The Uri to check.
* @return - Whether the Uri authority is Google Photos.
*/
private static boolean isGooglePhotosUri(Uri uri)
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Drive.
*/
private static boolean isGoogleDriveUri(Uri uri)
return "com.google.android.apps.docs.storage".equals(uri.getAuthority()) || "com.google.android.apps.docs.storage.legacy".equals(uri.getAuthority());
但现在根据 android 最新指南,您无法直接访问 getExternalStorageDirectory()。您需要为此实施解决方法
【讨论】:
我们可以使用 String strPath = FileUtils.getPath(uri)); 访问路径 以上代码的更新版本可以是:***.com/questions/13209494/… 太棒了。让我开心。 在不使用已弃用的 getExternalStorageDirectory()、@Nadeem Iqbal 的情况下,我们是否有相同的更新答案? —— @MdBasha : ***.com/questions/13209494/…【参考方案5】:试试这个。
public String getRealPathFromURI(Uri contentUri)
String[] proj = MediaStore.Audio.Media.DATA ;
Cursor cursor = managedQuery(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
【讨论】:
代码编辑经常被拒绝。 I noticed one suggesting an alternative function that is not deprecated 这给了我空光标,uri 是这样的 - file:/// @MartinSmith 仍然收到null
@AnkitaShah : 你必须尝试以下不同安卓版本的答案【参考方案6】:
您可以使用从不同的 SDK 版本获取文件路径
为此使用 RealPathUtils
public class RealPathUtils
@SuppressLint("NewApi")
public static String getRealPathFromURI_API19(Context context, Uri uri)
String filePath = "";
String wholeID = DocumentsContract.getDocumentId(uri);
// Split at colon, use second item in the array
String id = wholeID.split(":")[1];
String[] column = MediaStore.Images.Media.DATA ;
// where id is equal to
String sel = MediaStore.Images.Media._ID + "=?";
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
column, sel, new String[] id , null);
int columnIndex = cursor.getColumnIndex(column[0]);
if (cursor.moveToFirst())
filePath = cursor.getString(columnIndex);
cursor.close();
return filePath;
@SuppressLint("NewApi")
public static String getRealPathFromURI_API11to18(Context context, Uri contentUri)
String[] proj = MediaStore.Images.Media.DATA ;
String result = null;
CursorLoader cursorLoader = new CursorLoader(
context,
contentUri, proj, null, null, null);
Cursor cursor = cursorLoader.loadInBackground();
if(cursor != null)
int column_index =
cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
result = cursor.getString(column_index);
return result;
public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri)
String[] proj = MediaStore.Images.Media.DATA ;
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index
= cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
**现在从 URI 获取文件路径 **
String path = null;
if (Build.VERSION.SDK_INT < 11)
path = RealPathUtils.getRealPathFromURI_BelowAPI11(MainActivity.this, uri);
// SDK >= 11 && SDK < 19
else if (Build.VERSION.SDK_INT < 19)
path = RealPathUtils.getRealPathFromURI_API11to18(MainActivity.this, uri);
// SDK > 19 (Android 4.4)
else
path = RealPathUtils.getRealPathFromURI_API19(MainActivity.this, uri);
Log.d(TAG, "File Path: " + path);
// Get the file instance
File file = new File(path);
【讨论】:
这仅适用于 MediaStore.Images.Media 。如果您想获取其他文件,而不能与 MediaStore.FileColumns 一起使用,我将展示该过程! 这是完美的解决方案【参考方案7】:package com.utils;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.annotation.RequiresApi;
import android.text.TextUtils;
import android.util.Log;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class FileUtils
/* Get uri related content real local file path. */
public static String getPath(Context ctx, Uri uri)
String ret;
try
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
// Android OS above sdk version 19.
ret = getUriRealPathAboveKitkat(ctx, uri);
else
// Android OS below sdk version 19
ret = getRealPath(ctx.getContentResolver(), uri, null);
catch (Exception e)
e.printStackTrace();
Log.d("DREG", "FilePath Catch: " + e);
ret = getFilePathFromURI(ctx, uri);
return ret;
private static String getFilePathFromURI(Context context, Uri contentUri)
//copy file and send new file path
String fileName = getFileName(contentUri);
if (!TextUtils.isEmpty(fileName))
String TEMP_DIR_PATH = Environment.getExternalStorageDirectory().getPath();
File copyFile = new File(TEMP_DIR_PATH + File.separator + fileName);
Log.d("DREG", "FilePath copyFile: " + copyFile);
copy(context, contentUri, copyFile);
return copyFile.getAbsolutePath();
return null;
public static String getFileName(Uri uri)
if (uri == null) return null;
String fileName = null;
String path = uri.getPath();
int cut = path.lastIndexOf('/');
if (cut != -1)
fileName = path.substring(cut + 1);
return fileName;
public static void copy(Context context, Uri srcUri, File dstFile)
try
InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
if (inputStream == null) return;
OutputStream outputStream = new FileOutputStream(dstFile);
IOUtils.copyStream(inputStream, outputStream); // org.apache.commons.io
inputStream.close();
outputStream.close();
catch (Exception e) // IOException
e.printStackTrace();
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private static String getUriRealPathAboveKitkat(Context ctx, Uri uri)
String ret = "";
if (ctx != null && uri != null)
if (isContentUri(uri))
if (isGooglePhotoDoc(uri.getAuthority()))
ret = uri.getLastPathSegment();
else
ret = getRealPath(ctx.getContentResolver(), uri, null);
else if (isFileUri(uri))
ret = uri.getPath();
else if (isDocumentUri(ctx, uri))
// Get uri related document id.
String documentId = DocumentsContract.getDocumentId(uri);
// Get uri authority.
String uriAuthority = uri.getAuthority();
if (isMediaDoc(uriAuthority))
String idArr[] = documentId.split(":");
if (idArr.length == 2)
// First item is document type.
String docType = idArr[0];
// Second item is document real id.
String realDocId = idArr[1];
// Get content uri by document type.
Uri mediaContentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
if ("image".equals(docType))
mediaContentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
else if ("video".equals(docType))
mediaContentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
else if ("audio".equals(docType))
mediaContentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
// Get where clause with real document id.
String whereClause = MediaStore.Images.Media._ID + " = " + realDocId;
ret = getRealPath(ctx.getContentResolver(), mediaContentUri, whereClause);
else if (isDownloadDoc(uriAuthority))
// Build download uri.
Uri downloadUri = Uri.parse("content://downloads/public_downloads");
// Append download document id at uri end.
Uri downloadUriAppendId = ContentUris.withAppendedId(downloadUri, Long.valueOf(documentId));
ret = getRealPath(ctx.getContentResolver(), downloadUriAppendId, null);
else if (isExternalStoreDoc(uriAuthority))
String idArr[] = documentId.split(":");
if (idArr.length == 2)
String type = idArr[0];
String realDocId = idArr[1];
if ("primary".equalsIgnoreCase(type))
ret = Environment.getExternalStorageDirectory() + "/" + realDocId;
return ret;
/* Check whether this uri represent a document or not. */
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private static boolean isDocumentUri(Context ctx, Uri uri)
boolean ret = false;
if (ctx != null && uri != null)
ret = DocumentsContract.isDocumentUri(ctx, uri);
return ret;
/* Check whether this uri is a content uri or not.
* content uri like content://media/external/images/media/1302716
* */
private static boolean isContentUri(Uri uri)
boolean ret = false;
if (uri != null)
String uriSchema = uri.getScheme();
if ("content".equalsIgnoreCase(uriSchema))
ret = true;
return ret;
/* Check whether this uri is a file uri or not.
* file uri like file:///storage/41B7-12F1/DCIM/Camera/IMG_20180211_095139.jpg
* */
private static boolean isFileUri(Uri uri)
boolean ret = false;
if (uri != null)
String uriSchema = uri.getScheme();
if ("file".equalsIgnoreCase(uriSchema))
ret = true;
return ret;
/* Check whether this document is provided by ExternalStorageProvider. */
private static boolean isExternalStoreDoc(String uriAuthority)
boolean ret = false;
if ("com.android.externalstorage.documents".equals(uriAuthority))
ret = true;
return ret;
/* Check whether this document is provided by DownloadsProvider. */
private static boolean isDownloadDoc(String uriAuthority)
boolean ret = false;
if ("com.android.providers.downloads.documents".equals(uriAuthority))
ret = true;
return ret;
/* Check whether this document is provided by MediaProvider. */
private static boolean isMediaDoc(String uriAuthority)
boolean ret = false;
if ("com.android.providers.media.documents".equals(uriAuthority))
ret = true;
return ret;
/* Check whether this document is provided by google photos. */
private static boolean isGooglePhotoDoc(String uriAuthority)
boolean ret = false;
if ("com.google.android.apps.photos.content".equals(uriAuthority))
ret = true;
return ret;
/* Return uri represented document file real local path.*/
@SuppressLint("Recycle")
private static String getRealPath(ContentResolver contentResolver, Uri uri, String whereClause)
String ret = "";
// Query the uri with condition.
Cursor cursor = contentResolver.query(uri, null, whereClause, null, null);
if (cursor != null)
boolean moveToFirst = cursor.moveToFirst();
if (moveToFirst)
// Get columns name by uri type.
String columnName = MediaStore.Images.Media.DATA;
if (uri == MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
columnName = MediaStore.Images.Media.DATA;
else if (uri == MediaStore.Audio.Media.EXTERNAL_CONTENT_URI)
columnName = MediaStore.Audio.Media.DATA;
else if (uri == MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
columnName = MediaStore.Video.Media.DATA;
// Get column index.
int columnIndex = cursor.getColumnIndex(columnName);
// Get column value which is the uri related file local path.
ret = cursor.getString(columnIndex);
return ret;
在 build.gradle 文件中 添加这个
implementation 'org.apache.commons:commons-lang3:3.4'
现在从你的主班呼叫FileUtils.getPath(context, uri);
。
【讨论】:
此解决方案适用于为所有共享存储文件夹选择文件。我正在上传到 Firebase 存储并尝试了所有其他解决方案,但是当您从其他位置选择文件时,它们会导致 FileNotFoundException。这完美无缺【参考方案8】:我知道这已经得到解答。但是我在 cmets 中发现了一些问题。我从here 中找到了一个非常可靠的解决方案@
使用它 File file=FileUtils.getFile(uri);
public class FileUtils
//replace this with your authority
public static final String AUTHORITY = "com.ianhanniballake.localstorage.documents";
private FileUtils()
//private constructor to enforce Singleton pattern
/**
* TAG for log messages.
*/
static final String TAG = "FileUtils";
private static final boolean DEBUG = false; // Set to true to enable logging
/**
* @return Whether the URI is a local one.
*/
public static boolean isLocal(String url)
if (url != null && !url.startsWith("http://") && !url.startsWith("https://"))
return true;
return false;
public static boolean isLocalStorageDocument(Uri uri)
return AUTHORITY.equals(uri.getAuthority());
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
* @author paulburke
*/
public 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.
* @author paulburke
*/
public 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.
* @author paulburke
*/
public static boolean isMediaDocument(Uri uri)
return "com.android.providers.media.documents".equals(uri.getAuthority());
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri)
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
* @author paulburke
*/
public 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())
if (DEBUG)
DatabaseUtils.dumpCursor(cursor);
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
finally
if (cursor != null)
cursor.close();
return null;
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.<br>
* <br>
* Callers should check whether the path is local before assuming it
* represents a local file.
*
* @param context The context.
* @param uri The Uri to query.
* @author paulburke
* @see #isLocal(String)
* @see #getFile(Context, Uri)
*/
public static String getPath(final Context context, final Uri uri)
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri))
// LocalStorageProvider
if (isLocalStorageDocument(uri))
// The path is the id
return DocumentsContract.getDocumentId(uri);
// ExternalStorageProvider
else 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 the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
// File
else if ("file".equalsIgnoreCase(uri.getScheme()))
return uri.getPath();
return null;
/**
* Convert Uri into File, if possible.
*
* @return file A local file that the Uri was pointing to, or null if the
* Uri is unsupported or pointed to a remote resource.
* @author paulburke
* @see #getPath(Context, Uri)
*/
public static File getFile(Context context, Uri uri)
if (uri != null)
String path = getPath(context, uri);
if (path != null && isLocal(path))
return new File(path);
return null;
【讨论】:
感谢它的工作原理,但在外部存储器上没有得到 Android L 的完美路径 我没有添加完整的课程。检查链接并使用完整的课程。我相信它会解决你的问题。 是的,我也尝试过,但结果相同:( 当文件在外部 SD 卡上时,这似乎失败了(返回空值)。任何解决方案? @Snake 你找到解决办法了吗?【参考方案9】:您可以使用此功能从新 android 和旧版本的 uri 中获取文件
fun getFileFromUri(uri: Uri): File?
if (uri.path == null)
return null
var realPath = String()
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (uri.path!!.contains("/document/image:"))
databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
selection = "_id=?"
selectionArgs = arrayOf(DocumentsContract.getDocumentId(uri).split(":")[1])
else
databaseUri = uri
selection = null
selectionArgs = null
try
val column = "_data"
val projection = arrayOf(column)
val cursor = context.contentResolver.query(
databaseUri,
projection,
selection,
selectionArgs,
null
)
cursor?.let
if (it.moveToFirst())
val columnIndex = cursor.getColumnIndexOrThrow(column)
realPath = cursor.getString(columnIndex)
cursor.close()
catch (e: Exception)
Log.i("GetFileUri Exception:", e.message ?: "")
val path = if (realPath.isNotEmpty()) realPath else
when
uri.path!!.contains("/document/raw:") -> uri.path!!.replace(
"/document/raw:",
""
)
uri.path!!.contains("/document/primary:") -> uri.path!!.replace(
"/document/primary:",
"/storage/emulated/0/"
)
else -> return null
return File(path)
【讨论】:
它没有访问下载文件夹中的文件 你能告诉我你的代码吗?我使用此代码,它适用于所有文件夹和所有机器人 下面的 ans (***.com/a/55469368/9764941) 对我有用。谢谢【参考方案10】:收到文件路径时的代码片段。
Uri fileUri = data.getData();
FilePathHelper filePathHelper = new FilePathHelper();
String path = "";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT)
if (filePathHelper.getPathnew(fileUri, this) != null)
path = filePathHelper.getPathnew(fileUri, this).toLowerCase();
else
path = filePathHelper.getFilePathFromURI(fileUri, this).toLowerCase();
else
path = filePathHelper.getPath(fileUri, this).toLowerCase();
Bellow 是一个可以通过创建新对象来访问的类。您还需要在gradel implementation 'org.apache.directory.studio:org.apache.commons.io:2.4'
中添加一个依赖项
public class FilePathHelper
public FilePathHelper()
public String getMimeType(String url)
String type = null;
String extension = MimeTypeMap.getFileExtensionFromUrl(url.replace(" ", ""));
if (extension != null)
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
return type;
public String getFilePathFromURI(Uri contentUri, Context context)
//copy file and send new file path
String fileName = getFileName(contentUri);
if (!TextUtils.isEmpty(fileName))
File copyFile = new File(context.getExternalCacheDir() + File.separator + fileName);
copy(context, contentUri, copyFile);
return copyFile.getAbsolutePath();
return null;
public void copy(Context context, Uri srcUri, File dstFile)
try
InputStream inputStream = context.getContentResolver().openInputStream(srcUri);
if (inputStream == null) return;
OutputStream outputStream = new FileOutputStream(dstFile);
IOUtils.copy(inputStream, outputStream);
inputStream.close();
outputStream.close();
catch (IOException e)
e.printStackTrace();
public String getPath(Uri uri, Context context)
String filePath = null;
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
if (isKitKat)
filePath = generateFromKitkat(uri, context);
if (filePath != null)
return filePath;
Cursor cursor = context.getContentResolver().query(uri, new String[]MediaStore.MediaColumns.DATA, null, null, null);
if (cursor != null)
if (cursor.moveToFirst())
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA);
filePath = cursor.getString(columnIndex);
cursor.close();
return filePath == null ? uri.getPath() : filePath;
@TargetApi(19)
private String generateFromKitkat(Uri uri, Context context)
String filePath = null;
if (DocumentsContract.isDocumentUri(context, uri))
String wholeID = DocumentsContract.getDocumentId(uri);
String id = wholeID.split(":")[1];
String[] column = MediaStore.Video.Media.DATA;
String sel = MediaStore.Video.Media._ID + "=?";
Cursor cursor = context.getContentResolver().
query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
column, sel, new String[]id, null);
int columnIndex = cursor.getColumnIndex(column[0]);
if (cursor.moveToFirst())
filePath = cursor.getString(columnIndex);
cursor.close();
return filePath;
public String getFileName(Uri uri)
if (uri == null) return null;
String fileName = null;
String path = uri.getPath();
int cut = path.lastIndexOf('/');
if (cut != -1)
fileName = path.substring(cut + 1);
return fileName;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public String getPathnew(Uri uri, Context context)
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && 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 the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
// File
else if ("file".equalsIgnoreCase(uri.getScheme()))
return uri.getPath();
return null;
public 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 index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
catch (Exception e)
e.printStackTrace();
System.out.println("Something with exception - " + e.toString());
finally
if (cursor != null)
cursor.close();
return null;
public 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.
*/
public 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.
*/
public boolean isMediaDocument(Uri uri)
return "com.android.providers.media.documents".equals(uri.getAuthority());
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public boolean isGooglePhotosUri(Uri uri)
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
【讨论】:
你应该在filePathHelper中添加对SDK的检查【参考方案11】: public String getPath(Uri uri)
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
String document_id = cursor.getString(0);
document_id = document_id.substring(document_id.lastIndexOf(":") + 1);
cursor.close();
cursor = getContentResolver().query(
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
null, MediaStore.Images.Media._ID + " = ? ", new String[]document_id, null);
cursor.moveToFirst();
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
cursor.close();
return path;
使用这种方法我们可以从 Uri 中获取字符串文件路径。
【讨论】:
我希望如此,不要确认,因为这是我在 2020 年所做的并且工作得非常好。 它仅在您从最近的图像中选择图像时才有效,并且在您尝试从任何文件夹获取路径时总是崩溃 我搬到了颤振,所以我可能无法进一步帮助你,但如果你真的需要这个,请告诉我。【参考方案12】:要获取任何类型的文件路径,请使用此(取自https://github.com/iPaulPro/aFileChooser)
package com.yourpackage;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileFilter;
import java.text.DecimalFormat;
import java.util.Comparator;
import java.util.List;
/**
* @author Peli
* @author paulburke (ipaulpro)
* @version 2013-12-11
*/
public class FileUtils
private FileUtils()
//private constructor to enforce Singleton pattern
/**
* TAG for log messages.
*/
static final String TAG = "FileUtils";
private static final boolean DEBUG = true; // Set to true to enable logging
public static final String MIME_TYPE_AUDIO = "audio/*";
public static final String MIME_TYPE_TEXT = "text/*";
public static final String MIME_TYPE_IMAGE = "image/*";
public static final String MIME_TYPE_VIDEO = "video/*";
public static final String MIME_TYPE_APP = "application/*";
public static final String HIDDEN_PREFIX = ".";
/**
* Gets the extension of a file name, like ".png" or ".jpg".
*
* @param uri
* @return Extension including the dot("."); "" if there is no extension;
* null if uri was null.
*/
public static String getExtension(String uri)
if (uri == null)
return null;
int dot = uri.lastIndexOf(".");
if (dot >= 0)
return uri.substring(dot);
else
// No extension.
return "";
/**
* @return Whether the URI is a local one.
*/
public static boolean isLocal(String url)
if (url != null && !url.startsWith("http://") && !url.startsWith("https://"))
return true;
return false;
/**
* @return True if Uri is a MediaStore Uri.
* @author paulburke
*/
public static boolean isMediaUri(Uri uri)
return "media".equalsIgnoreCase(uri.getAuthority());
/**
* Convert File into Uri.
*
* @param file
* @return uri
*/
public static Uri getUri(File file)
if (file != null)
return Uri.fromFile(file);
return null;
/**
* Returns the path only (without file name).
*
* @param file
* @return
*/
public static File getPathWithoutFilename(File file)
if (file != null)
if (file.isDirectory())
// no file to be split off. Return everything
return file;
else
String filename = file.getName();
String filepath = file.getAbsolutePath();
// Construct path without file name.
String pathwithoutname = filepath.substring(0,
filepath.length() - filename.length());
if (pathwithoutname.endsWith("/"))
pathwithoutname = pathwithoutname.substring(0, pathwithoutname.length() - 1);
return new File(pathwithoutname);
return null;
/**
* @return The MIME type for the given file.
*/
public static String getMimeType(File file)
String extension = getExtension(file.getName());
if (extension.length() > 0)
return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1));
return "application/octet-stream";
/**
* @return The MIME type for the give Uri.
*/
public static String getMimeType(Context context, Uri uri)
File file = new File(getPath(context, uri));
return getMimeType(file);
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is @link LocalStorageProvider.
* @author paulburke
*/
public static boolean isLocalStorageDocument(Uri uri)
return LocalStorageProvider.AUTHORITY.equals(uri.getAuthority());
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
* @author paulburke
*/
public 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.
* @author paulburke
*/
public 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.
* @author paulburke
*/
public static boolean isMediaDocument(Uri uri)
return "com.android.providers.media.documents".equals(uri.getAuthority());
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri)
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
* @author paulburke
*/
public 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())
if (DEBUG)
DatabaseUtils.dumpCursor(cursor);
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
catch (Exception e)
e.printStackTrace();
finally
if (cursor != null)
cursor.close();
return null;
/**
* Get a file path from a Uri. This will quickGet the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.<br>
* <br>
* Callers should check whether the path is local before assuming it
* represents a local file.
*
* @param context The context.
* @param uri The Uri to query.
* @author paulburke
* @see #isLocal(String)
* @see #getFile(Context, Uri)
*/
public static String getPath(final Context context, final Uri uri)
if (DEBUG)
Log.d(TAG + " File -",
"Authority: " + uri.getAuthority() +
", Fragment: " + uri.getFragment() +
", Port: " + uri.getPort() +
", Query: " + uri.getQuery() +
", Scheme: " + uri.getScheme() +
", Host: " + uri.getHost() +
", Segments: " + uri.getPathSegments().toString()
);
// DocumentProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, uri))
// LocalStorageProvider
if (isLocalStorageDocument(uri))
// The path is the id
return DocumentsContract.getDocumentId(uri);
// ExternalStorageProvider
else 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];
//
return Environment.getExternalStorageDirectory() + "/" + split[1];
// TODO handle non-primary volumes
// DownloadsProvider
else if (isDownloadsDocument(uri))
try
final String id = DocumentsContract.getDocumentId(uri);
Log.d(TAG, "getPath: id= " + id);
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
catch (Exception e)
e.printStackTrace();
List<String> segments = uri.getPathSegments();
if(segments.size() > 1)
String rawPath = segments.get(1);
if(!rawPath.startsWith("/"))
return rawPath.substring(rawPath.indexOf("/"));
else
return rawPath;
// 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 the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
// File
else if ("file".equalsIgnoreCase(uri.getScheme()))
return uri.getPath();
return null;
/**
* Convert Uri into File, if possible.
*
* @return file A local file that the Uri was pointing to, or null if the
* Uri is unsupported or pointed to a remote resource.
* @author paulburke
* @see #getPath(Context, Uri)
*/
public static File getFile(Context context, Uri uri)
if (uri != null)
String path = getPath(context, uri);
if (path != null && isLocal(path))
return new File(path);
return null;
/**
* Get the file size in a human-readable string.
*
* @param size
* @return
* @author paulburke
*/
public static String getReadableFileSize(int size)
final int BYTES_IN_KILOBYTES = 1024;
final DecimalFormat dec = new DecimalFormat("###.#");
final String KILOBYTES = " KB";
final String MEGABYTES = " MB";
final String GIGABYTES = " GB";
float fileSize = 0;
String suffix = KILOBYTES;
if (size > BYTES_IN_KILOBYTES)
fileSize = size / BYTES_IN_KILOBYTES;
if (fileSize > BYTES_IN_KILOBYTES)
fileSize = fileSize / BYTES_IN_KILOBYTES;
if (fileSize > BYTES_IN_KILOBYTES)
fileSize = fileSize / BYTES_IN_KILOBYTES;
suffix = GIGABYTES;
else
suffix = MEGABYTES;
return String.valueOf(dec.format(fileSize) + suffix);
/**
* Attempt to retrieve the thumbnail of given File from the MediaStore. This
* should not be called on the UI thread.
*
* @param context
* @param file
* @return
* @author paulburke
*/
public static Bitmap getThumbnail(Context context, File file)
return getThumbnail(context, getUri(file), getMimeType(file));
/**
* Attempt to retrieve the thumbnail of given Uri from the MediaStore. This
* should not be called on the UI thread.
*
* @param context
* @param uri
* @return
* @author paulburke
*/
public static Bitmap getThumbnail(Context context, Uri uri)
return getThumbnail(context, uri, getMimeType(context, uri));
/**
* Attempt to retrieve the thumbnail of given Uri from the MediaStore. This
* should not be called on the UI thread.
*
* @param context
* @param uri
* @param mimeType
* @return
* @author paulburke
*/
public static Bitmap getThumbnail(Context context, Uri uri, String mimeType)
if (DEBUG)
Log.d(TAG, "Attempting to quickGet thumbnail");
if (!isMediaUri(uri))
Log.e(TAG, "You can only retrieve thumbnails for images and videos.");
return null;
Bitmap bm = null;
if (uri != null)
final ContentResolver resolver = context.getContentResolver();
Cursor cursor = null;
try
cursor = resolver.query(uri, null, null, null, null);
if (cursor.moveToFirst())
final int id = cursor.getInt(0);
if (DEBUG)
Log.d(TAG, "Got thumb ID: " + id);
if (mimeType.contains("video"))
bm = MediaStore.Video.Thumbnails.getThumbnail(
resolver,
id,
MediaStore.Video.Thumbnails.MINI_KIND,
null);
else if (mimeType.contains(FileUtils.MIME_TYPE_IMAGE))
bm = MediaStore.Images.Thumbnails.getThumbnail(
resolver,
id,
MediaStore.Images.Thumbnails.MINI_KIND,
null);
catch (Exception e)
if (DEBUG)
Log.e(TAG, "getThumbnail", e);
finally
if (cursor != null)
cursor.close();
return bm;
/**
* File and folder comparator. TODO Expose sorting option method
*
* @author paulburke
*/
public static Comparator<File> sComparator = new Comparator<File>()
@Override
public int compare(File f1, File f2)
// Sort alphabetically by lower case, which is much cleaner
return f1.getName().toLowerCase().compareTo(
f2.getName().toLowerCase());
;
/**
* File (not directories) filter.
*
* @author paulburke
*/
public static FileFilter sFileFilter = new FileFilter()
@Override
public boolean accept(File file)
final String fileName = file.getName();
// Return files only (not directories) and skip hidden files
return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX);
;
/**
* Folder (directories) filter.
*
* @author paulburke
*/
public static FileFilter sDirFilter = new FileFilter()
@Override
public boolean accept(File file)
final String fileName = file.getName();
// Return directories only and skip hidden directories
return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX);
;
/**
* Get the Intent for selecting content to be used in an Intent Chooser.
*
* @return The intent for opening a file with Intent.createChooser()
* @author paulburke
*/
public static Intent createGetContentIntent()
// Implicitly allow the user to select a particular kind of data
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// The MIME data type filter
intent.setType("*/*");
// Only return URIs that can be opened with ContentResolver
intent.addCategory(Intent.CATEGORY_OPENABLE);
return intent;
【讨论】:
@K40S,现在可以从许多来源获得此代码。虽然我从另一个来源获取,但无法记住它。但是,我已在我的解决方案中添加了您的来源。在这方面,被盗是一个严厉的词。 在没有信用的情况下采取,而违反许可条款(将许可附加到代码)是在窃取。我不想羞辱你什么的,只是想用正确的词:) 如果您看到粘贴的代码,您会看到与真实作者相关的所有 cmets 都不会从该答案中删除。我故意保留它,很抱歉没有给出确切的链接。 使用这个文件:drive.google.com/open?id=1u5QC5omPLnteWy_luBPZ8l5aXMPB--nA 在那个类中使用你的包名和 R.java 文件。【参考方案13】:对于 Kotlin:
只需创建一个名为 URIPathHelper.kt 的新文件。然后将以下实用程序类复制并粘贴到您的文件中。它涵盖了所有场景,适用于所有 Android 版本。它的解释将在后面讨论。
package com.mvp.handyopinion
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
class URIPathHelper
fun getPath(context: Context, uri: Uri): String?
val isKitKatorAbove = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
// DocumentProvider
if (isKitKatorAbove && DocumentsContract.isDocumentUri(context, uri))
// ExternalStorageProvider
if (isExternalStorageDocument(uri))
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true))
return Environment.getExternalStorageDirectory().toString() + "/" + split[1]
else if (isDownloadsDocument(uri))
val id = DocumentsContract.getDocumentId(uri)
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id))
return getDataColumn(context, contentUri, null, null)
else if (isMediaDocument(uri))
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).toTypedArray()
val type = split[0]
var contentUri: Uri? = null
if ("image" == type)
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
else if ("video" == type)
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
else if ("audio" == type)
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
return getDataColumn(context, contentUri, selection, selectionArgs)
else if ("content".equals(uri.scheme, ignoreCase = true))
return getDataColumn(context, uri, null, null)
else if ("file".equals(uri.scheme, ignoreCase = true))
return uri.path
return null
fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array<String>?): String?
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(column)
try
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,null)
if (cursor != null && cursor.moveToFirst())
val column_index: Int = cursor.getColumnIndexOrThrow(column)
return cursor.getString(column_index)
finally
if (cursor != null) cursor.close()
return null
fun isExternalStorageDocument(uri: Uri): Boolean
return "com.android.externalstorage.documents" == uri.authority
fun isDownloadsDocument(uri: Uri): Boolean
return "com.android.providers.downloads.documents" == uri.authority
fun isMediaDocument(uri: Uri): Boolean
return "com.android.providers.media.documents" == uri.authority
如何使用 URIPathHelper 类从 URI 中获取路径
val uriPathHelper = URIPathHelper()
val filePath = uriPathHelper.getPath(this, YOUR_URI_OBJECT)
【讨论】:
引起:java.lang.IllegalArgumentException:列'_data'不存在。可用列:[] 我在尝试添加音频文件时遇到此错误Environment.getExternalStorageDirectory()
已弃用【参考方案14】:
唯一的问题是获取下载目录的路径或msf和NumberFormat异常,试试这个。它对我很有效
package com.example.bookingmelbourne;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class FileUtils
private static final String TAG = "FileUtils";
@WorkerThread
@Nullable
public static String getReadablePathFromUri(Context context, Uri uri)
String path = null;
if ("file".equalsIgnoreCase(uri.getScheme()))
path = uri.getPath();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT)
path = getPath(context, uri);
if (TextUtils.isEmpty(path))
return path;
Log.d(TAG, "get path from uri: " + path);
if (!isReadablePath(path))
int index = path.lastIndexOf("/");
String name = path.substring(index + 1);
String dstPath = context.getCacheDir().getAbsolutePath() + File.separator + name;
if (copyFile(context, uri, dstPath))
path = dstPath;
Log.d(TAG, "copy file success: " + path);
else
Log.d(TAG, "copy file fail!");
return path;
public static String getPath(final Context context, final Uri uri)
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
if (isKitKat && DocumentsContract.isDocumentUri(context, uri))
if (isExternalStorageDocument(uri))
final String docId = DocumentsContract.getDocumentId(uri);
Log.d("External Storage", docId);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type))
return Environment.getExternalStorageDirectory() + "/" + split[1];
else if (isDownloadsDocument(uri))
String dstPath = context.getCacheDir().getAbsolutePath() + File.separator + getFileName(context,uri);
if (copyFile(context, uri, dstPath))
Log.d(TAG, "copy file success: " + dstPath);
return dstPath;
else
Log.d(TAG, "copy file fail!");
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);
else if ("content".equalsIgnoreCase(uri.getScheme()))
return getDataColumn(context, uri, null, null);
else if ("file".equalsIgnoreCase(uri.getScheme()))
return uri.getPath();
return null;
public static String getFileName(Context context, Uri uri)
Cursor cursor = context.getContentResolver().query(uri,null,null,null,null);
int nameindex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
cursor.moveToFirst();
return cursor.getString(nameindex);
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());
private static boolean isReadablePath(@Nullable String path)
if (TextUtils.isEmpty(path))
return false;
boolean isLocalPath;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
if (!TextUtils.isEmpty(path))
File localFile = new File(path);
isLocalPath = localFile.exists() && localFile.canRead();
else
isLocalPath = false;
else
isLocalPath = path.startsWith(File.separator);
return isLocalPath;
private static boolean copyFile(Context context, Uri uri, String dstPath)
InputStream inputStream = null;
OutputStream outputStream = null;
try
inputStream = context.getContentResolver().openInputStream(uri);
outputStream = new FileOutputStream(dstPath);
byte[] buff = new byte[100 * 1024];
int len;
while ((len = inputStream.read(buff)) != -1)
outputStream.write(buff, 0, len);
catch (Exception e)
e.printStackTrace();
return false;
finally
if (inputStream != null)
try
inputStream.close();
catch (Exception e)
e.printStackTrace();
if (outputStream != null)
try
outputStream.close();
catch (Exception e)
e.printStackTrace();
return true;
之后,你可以调用这个方法传递上下文和uri对象
FileUtils.getReadablePathFromUri(context,uri)
【讨论】:
非常感谢这个解决方案,它运行良好,我使用了这个代码。 它没有显示正确的路径【参考方案15】:由于这里的大多数解决方案都不适用于不推荐使用 Environment.getExternalStorageDirectory()
的 API 30+,或者解析作为系统内部信息的文档 id,因此值得一问:您真的需要 em> 路径?
如果您只是需要一种访问项目数据的方法,那么在所有平台上都有一种直接的方法:
fun getInputStream(context: Context, uri: Uri): InputStream?
return context.contentResolver.openInputStream(uri)
如果需要显示名(如test.jpg),也可以使用内容解析器:
fun getDisplayName(context: Context, uri: Uri): String?
context.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)
.use
if (it == null || !it.moveToFirst())
return null
val columnIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (columnIndex == -1)
return null
return it.getString(columnIndex)
只是想我会提到它,因为它会对我有所帮助。
【讨论】:
【参考方案16】:我们都同意 SAF 的设计非常糟糕且缓慢,但 Google 一直在推动我们这样做。鉴于getExternalStorageDirectory()
早已被弃用,并且可能从 11 年起可能无法使用,我宁愿不使用这些解决方案,因为它距离崩溃只有一次更新......
【讨论】:
那么您的建议答案是什么?你应该提供一个可靠的答案和解决方案我不是吗?你在这里表达你的意见“作为一个答案” @elliotching 同意,然后给我投反对票。我很沮丧(因为人们仍在努力更新代码),情绪激动(我遇到了同样的问题,并且无法接受一个骇人听闻的解决方案),然后输入了这个。在我的辩护中,答案是骇人听闻的,不可靠的,不可测试的。为什么我不编辑答案:太多了,所以要求答案不会被彻底改变。为什么我没有评论:太多了。【参考方案17】:我更喜欢使用Simple Storage:
// For document file
val documentFile = DocumentFileCompat.fromUri(context, uri)
val path = documentFile.absolutePath // e.g. /storage/emulated/0/Music/Torisetsu.mp3
// For media file
val mediaFile = MediaFile(context, uri)
val path = mediaFile.absolutePath // e.g. /storage/emulated/0/Music/My Love.mp3
要检查URI是媒体文件还是文档文件,使用isMediaDocument
扩展函数:
val isMediaFile = uri.isMediaDocument
【讨论】:
【参考方案18】:这节省了我的时间。从 URI 获取路径的最简单方法。
//kotlin
myuri = data.data
val realPath = myuri.path
Log.d(TAG, "path: $realPath")
返回路径:
path: /storage/emulated/0/Download/CutOFF - Escuro (Original Mix).mp3
【讨论】:
某些设备返回“content://...”作为您的realPath
,它根本无法正常工作【参考方案19】:
适用于所有 api 版本(在 Android 10 上测试)
val returnCursor: Cursor? = context.contentResolver.query(uri, null, null, null, null)
val columnIndex = returnCursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
returnCursor.moveToFirst();
val path = returnCursor.getString(columnIndex)
【讨论】:
【参考方案20】:如果您有权访问文件并希望避免使用ContentResolver
或直接读取文件,请参阅答案:https://***.com/a/67499473/6367262
【讨论】:
【参考方案21】:如果你的目标是 android 11 (api level 30) 或更高版本,即使你从 uri 中获取文件路径,你也无法访问该文件,你会发现 FileNotFoundException (permission denied)
因为从 android 11 开始,应用程序只允许从自己的应用程序特定目录(例如:Android/data/packagename)访问文件。
不过有个权限叫
MANAGE_EXTERNAL_STORAGE
允许应用访问所有存储文件。但是,如果您想将您的应用发布到 Play 商店,根据商店政策,此权限不适用于所有类型的应用。检查这个link
那么解决办法是什么?
您可以从 URI 获取输入流并使用它,如果它适合您的用例(例如通过改造上传文件,转换为位图等)
如果您迫切需要 File 对象,那么您必须将输入流转换为文件并将其存储在您的应用程序特定目录中。
object FileUtils
val cRes = BaseApplication.instance.contentResolver
@Throws(IOException::class)
fun getInputStream(uri:Uri): InputStream?
return if (isVirtualFile(uri))
getInputStreamForVirtualFile(uri, getMimeType(uri))
else
cRes.openInputStream(uri)
fun getMimeType(uri: Uri): String?
return cRes.getType(uri)
private fun isVirtualFile(uri: Uri): Boolean
if (!DocumentsContract.isDocumentUri(BaseApplication.instance, uri))
return false
val cursor: Cursor? = cRes.query(
uri,
arrayOf(DocumentsContract.Document.COLUMN_FLAGS),
null,
null,
null
)
val flags: Int = cursor?.use
if (cursor.moveToFirst())
cursor.getInt(0)
else
0
?: 0
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
flags and DocumentsContract.Document.FLAG_VIRTUAL_DOCUMENT != 0
else
return false
@Throws(IOException::class)
private fun getInputStreamForVirtualFile(
uri: Uri, mimeTypeFilter: String?): FileInputStream?
if (mimeTypeFilter==null)
throw FileNotFoundException()
val openableMimeTypes: Array<String>? =
cRes.getStreamTypes(uri, mimeTypeFilter)
return if (openableMimeTypes?.isNotEmpty() == true)
cRes
.openTypedAssetFileDescriptor(uri, openableMimeTypes[0], null)?.createInputStream()
else
throw FileNotFoundException()
fun copyStreamToFile(inputStream: InputStream, outputFile: File)
inputStream.use input ->
val outputStream = FileOutputStream(outputFile)
outputStream.use output ->
val buffer = ByteArray(4 * 1024) // buffer size
while (true)
val byteCount = input.read(buffer)
if (byteCount < 0) break
output.write(buffer, 0, byteCount)
output.flush()
打开文件选择器时使用这个
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
没有
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
【讨论】:
【参考方案22】:@S.A.Parkhid 在 kotlin 上的回答。
class FileUtils @Inject constructor(private val context: Context)
private var selection: String? = null
private var selectionArgs: Array<String>? = null
fun getFile(uri: Uri): File?
val path = getPath(uri)
return if (path != null)
File(path)
else
null
fun getPath(uri: Uri): String?
// check here to KITKAT or new version
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
getPathForKitKatAndAbove(uri)
else
getPathBelowKitKat(uri)
@SuppressLint("NewApi")
private fun handleExternalStorage(uri: Uri): String?
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).toTypedArray()
val fullPath = getPathFromExtSD(split)
return if (fullPath !== "")
fullPath
else
null
@SuppressLint("NewApi")
private fun handleDownloads(uri: Uri): String?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
handleDownloads23ApiAndAbove(uri)
else
handleDownloadsBelow23Api(uri)
return null
@SuppressLint("NewApi")
private fun handleDownloadsBelow23Api(uri: Uri): String?
val id = DocumentsContract.getDocumentId(uri)
if (id.startsWith("raw:"))
return id.replaceFirst("raw:".toRegex(), "")
var contentUri: Uri? = null
try
contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), id.toLong()
)
catch (e: NumberFormatException)
log(e)
if (contentUri != null)
return getDataColumn(contentUri)
return null
@SuppressLint("NewApi")
private fun handleDownloads23ApiAndAbove(uri: Uri): String?
var cursor: Cursor? = null
try
cursor = context.contentResolver
.query(uri, arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), null, null, null)
if (cursor != null && cursor.moveToFirst())
val fileName = cursor.getString(0)
val path: String =
Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName
if (path.isNotEmpty())
return path
finally
cursor?.close()
val id: String = DocumentsContract.getDocumentId(uri)
if (id.isNotEmpty())
if (id.startsWith("raw:"))
return id.replaceFirst("raw:".toRegex(), "")
val contentUriPrefixesToTry = arrayOf(
"content://downloads/public_downloads",
"content://downloads/my_downloads"
)
for (contentUriPrefix in contentUriPrefixesToTry)
return try
val contentUri = ContentUris.withAppendedId(
Uri.parse(contentUriPrefix),
id.toLong()
)
getDataColumn(contentUri)
catch (e: NumberFormatException)
//In Android 8 and Android P the id is not a number
uri.path.orEmpty().replaceFirst("^/document/raw:".toRegex(), "")
.replaceFirst("^raw:".toRegex(), "")
return null
@SuppressLint("NewApi")
private fun handleMedia(uri: Uri): String?
val docId = DocumentsContract.getDocumentId(uri)
val split = docId.split(":".toRegex()).toTypedArray()
val contentUri: Uri? = when (split[0])
"image" ->
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
"video" ->
MediaStore.Video.Media.EXTERNAL_CONTENT_URI
"audio" ->
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
else -> null
selection = "_id=?"
selectionArgs = arrayOf(split[1])
return if (contentUri != null)
getDataColumn(contentUri)
else
null
private fun handleContentScheme(uri: Uri): String?
if (isGooglePhotosUri(uri))
return uri.lastPathSegment
if (isGoogleDriveUri(uri))
return getDriveFilePath(uri)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
copyFileToInternalStorage(uri, "userfiles")
else
getDataColumn(uri)
@SuppressLint("NewApi")
private fun getPathForKitKatAndAbove(uri: Uri): String?
return when
// ExternalStorageProvider
isExternalStorageDocument(uri) -> handleExternalStorage(uri)
// DownloadsProvider
isDownloadsDocument(uri) -> handleDownloads(uri)
// MediaProvider
isMediaDocument(uri) -> handleMedia(uri)
//GoogleDriveProvider
isGoogleDriveUri(uri) -> getDriveFilePath(uri)
//WhatsAppProvider
isWhatsAppFile(uri) -> getFilePathForWhatsApp(uri)
//ContentScheme
"content".equals(uri.scheme, ignoreCase = true) -> handleContentScheme(uri)
//FileScheme
"file".equals(uri.scheme, ignoreCase = true) -> uri.path
else -> null
private fun getPathBelowKitKat(uri: Uri): String?
if (isWhatsAppFile(uri))
return getFilePathForWhatsApp(uri)
if ("content".equals(uri.scheme, ignoreCase = true))
val projection = arrayOf(
MediaStore.Images.Media.DATA
)
var cursor: Cursor? = null
try
cursor = context.contentResolver
.query(uri, projection, selection, selectionArgs, null)
val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
if (cursor.moveToFirst())
return cursor.getString(columnIndex)
catch (e: IOException)
log(e)
finally
cursor?.close()
return null
private fun fileExists(filePath: String): Boolean
val file = File(filePath)
return file.exists()
private fun getPathFromExtSD(pathData: Array<String>): String
val type = pathData[0]
val relativePath = "/" + pathData[1]
var fullPath: String
// on my Sony devices (4.4.4 & 5.1.1), `type` is a dynamic string
// something like "71F8-2C0A", some kind of unique id per storage
// don't know any API that can get the root path of that storage based on its id.
//
// so no "primary" type, but let the check here for other devices
if ("primary".equals(type, ignoreCase = true))
fullPath = Environment.getExternalStorageDirectory().toString() + relativePath
if (fileExists(fullPath))
return fullPath
// Environment.isExternalStorageRemovable() is `true` for external and internal storage
// so we cannot relay on it.
//
// instead, for each possible path, check if file exists
// we'll start with secondary storage as this could be our (physically) removable sd card
fullPath = System.getenv("SECONDARY_STORAGE").orEmpty() + relativePath
if (fileExists(fullPath))
return fullPath
fullPath = System.getenv("EXTERNAL_STORAGE").orEmpty() + relativePath
return if (fileExists(fullPath))
fullPath
else fullPath
private fun getDriveFilePath(uri: Uri): String?
context.contentResolver.query(
uri, null, null, null, null
)?.use cursor ->
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
val name = cursor.getString(nameIndex)
val file = File(context.cacheDir, name)
try
val inputStream = context.contentResolver.openInputStream(uri)!!
val outputStream = FileOutputStream(file)
val bytesAvailable = inputStream.available()
val bufferSize = min(bytesAvailable, MAX_BUFFER_SIZE)
val buffers = ByteArray(bufferSize)
var read: Int
while (inputStream.read(buffers).also read = it != -1)
outputStream.write(buffers, 0, read)
inputStream.close()
outputStream.close()
catch (e: IOException)
log(e)
finally
cursor.close()
return file.path
return null
/***
* Used for Android Q+
* @param uri
* @param newDirName if you want to create a directory, you can set this variable
* @return
*/
private fun copyFileToInternalStorage(uri: Uri, newDirName: String): String?
context.contentResolver.query(
uri, arrayOf(OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE), null, null, null
)?.use cursor ->
/*
* Get the column indexes of the data in the Cursor,
* * move to the first row in the Cursor, get the data,
* * and display it.
* */
val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
cursor.moveToFirst()
val name = cursor.getString(nameIndex)
val output: File = if (newDirName != "")
val dir = File(context.filesDir.toString() + "/" + newDirName)
if (!dir.exists())
dir.mkdir()
File(context.filesDir.toString() + "/" + newDirName + "/" + name)
else
File(context.filesDir.toString() + "/" + name)
try
val inputStream = context.contentResolver.openInputStream(uri) ?: return null
val outputStream = FileOutputStream(output)
var read: Int
val buffers = ByteArray(BUFFER_SIZE)
while (inputStream.read(buffers).also read = it != -1)
outputStream.write(buffers, 0, read)
inputStream.close()
outputStream.close()
catch (e: IOException)
log(e)
finally
cursor.close()
return output.path
return null
private fun getFilePathForWhatsApp(uri: Uri): String?
return copyFileToInternalStorage(uri, "whatsapp")
private fun getDataColumn(uri: Uri): String?
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(column)
try
cursor = context.contentResolver.query(
uri, projection,
selection, selectionArgs, null
)
if (cursor != null && cursor.moveToFirst())
val index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(index)
finally
cursor?.close()
return null
private fun isExternalStorageDocument(uri: Uri): Boolean
return EXTERNAL_STORAGE_CONTENT == uri.authority
private fun isDownloadsDocument(uri: Uri): Boolean
return DOWNLOAD_DOCUMENT_CONTENT == uri.authority
private fun isMediaDocument(uri: Uri): Boolean
return MEDIA_DOCUMENT_CONTENT == uri.authority
private fun isGooglePhotosUri(uri: Uri): Boolean
return GOOGLE_PHOTOS_CONTENT == uri.authority
private fun isWhatsAppFile(uri: Uri): Boolean
return WHATS_APP_CONTENT == uri.authority
private fun isGoogleDriveUri(uri: Uri): Boolean
return GOOGLE_DRIVE_CONTENT == uri.authority || "com.google.android.apps.docs.storage.legacy" == uri.authority
companion object
private const val BUFFER_SIZE = 1024
private const val MAX_BUFFER_SIZE = 1024 * 1024
private const val GOOGLE_DRIVE_CONTENT = "com.google.android.apps.docs.storage"
private const val WHATS_APP_CONTENT = "com.whatsapp.provider.media"
private const val GOOGLE_PHOTOS_CONTENT = "com.google.android.apps.photos.content"
private const val MEDIA_DOCUMENT_CONTENT = "com.android.providers.media.documents"
private const val DOWNLOAD_DOCUMENT_CONTENT = "com.android.providers.downloads.documents"
private const val EXTERNAL_STORAGE_CONTENT = "com.android.externalstorage.documents"
【讨论】:
【参考方案23】:我很难在 Xamarin 上解决这个问题。根据上面的建议,我想出了这个解决方案。
private string getRealPathFromURI(Android.Net.Uri contentUri)
string filename = "";
string thepath = "";
Android.Net.Uri filePathUri;
ICursor cursor = this.ContentResolver.Query(contentUri, null, null, null, null);
if (cursor.MoveToFirst())
int column_index = cursor.GetColumnIndex(MediaStore.Images.Media.InterfaceConsts.Data);//Instead of "MediaStore.Images.Media.DATA" can be used "_data"
filePathUri = Android.Net.Uri.Parse(cursor.GetString(column_index));
filename = filePathUri.LastPathSegment;
thepath = filePathUri.Path;
return thepath;
【讨论】:
Android 7.0
Column_Index 在使用ActionContentGet
选择文件时为-1
并键入*/*
让我们说PDF【参考方案24】:
String uri_path = "file:///mnt/sdcard/FileName.mp3";
File f = new removeUriFromPath(uri_path));
public static String removeUriFromPath(String uri)
return uri.substring(7, uri.length());
【讨论】:
我的兄弟使用子字符串不是解决方案,因为 uri_path 是动态的! @sanjeev badoni,考虑删除您的答案以恢复您的声誉以上是关于如何从 URI 获取完整的文件路径的主要内容,如果未能解决你的问题,请参考以下文章