如何从 URI 获取完整文件路径

我希望从 URI 获取完整的文件路径。URI 不是一个图像,但它是一个音乐文件,但如果我像 MediaStore 解决方案那样做,如果应用程序用户选择 Astro 作为浏览器,而不是 Music Player,它就不会工作。我要怎么解决这个问题?

385639 次浏览

试试这个。

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);
}

用途:

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));
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());
}

您可以使用来自不同 SDk 版本的 get File path

使用 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 获取文件 Path

 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);

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).

以上代码将在奥利奥和如果它是低于奥利奥比 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());
}
}

我在 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;
}

我知道这个问题已经得到了回答,但是我在评论中发现了一些问题。我发现一个伟大的可靠的解决方案从 给你分叉

使用它 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;
}




}
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);

接收文件路径时的代码段。

 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 是一个可以通过创建新对象来访问的类。您还需要在级别 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());
}
}

从 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 ()。您需要为此实现一个变通方法

要获取任何类型的文件路径,请使用以下命令(取自 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;
}
}

当前页面(这个)上存在的一个答案是正确的,但它有一些错误。例如,它不能在 API 29 + 的设备上工作。 我会更新上面的代码并发布它的新版本。 我认为这个帖子应该标记为最终答案。

你也可以从我的 GitHub 回购中下载代码:

GitHub 仓库

你可以让我更新代码。

更新代码: (增加了对 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());
}




}

答案是“ 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"
}
}
  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 获得字符串文件路径。

对科特林来说:

只需创建一个名为 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)

我们都同意 SAF 和 慢慢来的设计非常糟糕,但是 Google 一直在推动我们这么做。考虑到 getExternalStorageDirectory()已经被废弃很久了,而且可能从11号起就不能工作了,我宁愿不使用这些解决方案,因为它离崩溃只有一个更新..。

我更喜欢使用 简易存储:

// 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

从 URI 获取路径的最简单方法。

  //kotlin
myuri = data.data
val realPath = myuri.path
Log.d(TAG, "path: $realPath")

返回路径:

 path: /storage/emulated/0/Download/CutOFF - Escuro (Original Mix).mp3

适用于所有 API 版本(在 Android10上测试)

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)

如果您有访问文件的权限,并且希望避免使用 ContentResolver或直接读取文件,请参阅答案: https://stackoverflow.com/a/67499473/6367262

唯一的问题是获取下载目录或 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)

你可以使用这个函数在新的和旧的机器人中从 uri 获取文件

fun getFileFromUri(context: Context, uri: Uri?): File? {
uri ?: return null
uri.path ?: return null


var newUriString = uri.toString()
newUriString = newUriString.replace(
"content://com.android.providers.downloads.documents/",
"content://com.android.providers.media.documents/"
)
newUriString = newUriString.replace(
"/msf%3A", "/image%3A"
)
val newUri = Uri.parse(newUriString)


var realPath = String()
val databaseUri: Uri
val selection: String?
val selectionArgs: Array<String>?
if (newUri.path?.contains("/document/image:") == true) {
databaseUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
selection = "_id=?"
selectionArgs = arrayOf(DocumentsContract.getDocumentId(newUri).split(":")[1])
} else {
databaseUri = newUri
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 = realPath.ifEmpty {
when {
newUri.path?.contains("/document/raw:") == true -> newUri.path?.replace(
"/document/raw:",
""
)
newUri.path?.contains("/document/primary:") == true -> newUri.path?.replace(
"/document/primary:",
"/storage/emulated/0/"
)
else -> return null
}
}
return if (path.isNullOrEmpty()) null else File(path)
}

如果您的目标是 android 11(api 级别30)或更高,即使您从 uri 获得文件路径,您也无法访问该文件,并且您将发现 FileNotFoundException (权限拒绝)

因为在 Android 中,11个应用程序只允许从自己的应用程序特定目录(例如: Android/data/packagename)访问文件。

但是有一个权限叫做

管理 _ 外部 _ 存储

它允许应用程序访问所有存储文件。但是,如果你想发布你的应用程序播放商店,根据商店的政策,这一权限是不允许所有类型的应用程序。看看这个 链接

那解决办法是什么?

您可以从 URI 获得 inputstream,如果它适合您的用例(通过改造上传文件,转换为位图等) ,则可以使用它

如果你非常需要 File 对象,那么你必须将输入流转换为 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);

因为这里的大多数解决方案都不能在不推荐使用 Environment.getExternalStorageDirectory()的 API 30 + 上工作,或者解析系统内部信息的文档 id,所以值得一问: 您真的使用 需要路径吗?

如果你只是需要一种方法来访问项目的数据,有一种简单的方法可以在所有平台上实现:

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)
}
}


我只是觉得我应该提一下,因为这会对我有帮助。

Java:

InputStream getInputStream(Context context, Uri uri)
{
return context.getContentResolver().openInputStream(uri)
}

对于那些需要知道流程长度的人:

InputStream is = getInputStream(context, uri);
int length = is.available();

它支持机器人11。

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.util.Log;


import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;


public class GetFile {
public static File getFile(Context context, Uri uri) {
File destinationFilename = new File(context.getFilesDir().getPath() + File.separatorChar + queryName(context, uri));


try (InputStream ins = context.getContentResolver().openInputStream(uri)) {
createFileFromStream(ins, destinationFilename);
} catch (Exception ex) {
Log.e("Save File", ex.getMessage());
ex.printStackTrace();
}
return destinationFilename;
}


public static void createFileFromStream(InputStream ins, File destination) {
try (OutputStream os = new FileOutputStream(destination)) {
byte[] buffer = new byte[4096];
int length;
while ((length = ins.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
} catch (Exception ex) {
Log.e("Save File", ex.getMessage());
ex.printStackTrace();
}
}


public static String queryName(Context context, Uri uri) {
String result = null;
if (uri.getScheme().equals("content")) {
try (Cursor cursor = context.getContentResolver().query(uri, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
result = cursor.getString(nameIndex);
}
}
}
if (result == null) {
result = uri.getLastPathSegment();
}
return result;
}
}