android uri 参数,Android7.0以上Uri转路径的方法实现(已验证)

网络上看到过很多种Uri转路径的方法,可基本上都只适用于很少的Uri值,可能没有结果(例如,对于由MediaStore索引的非本地文件),也可能没有可用的结果(例如,对于可移动存储上的文件)。

解决方法

使用ContentResolver和openInputStream()在Uri标识的内容上获取InputStream。在控制的文件上使用InputStream和FileOutputStream复制内容,然后使用该文件。

代码如下:

private static String getFilePathForN(Context context, Uri uri) {

try {

Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null);

int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);

returnCursor.moveToFirst();

String name = (returnCursor.getString(nameIndex));

File file = new File(context.getFilesDir(), name);

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 = Math.min(bytesAvailable, maxBufferSize);

final byte[] buffers = new byte[bufferSize];

while ((read = inputStream.read(buffers)) != -1) {

outputStream.write(buffers, 0, read);

}

returnCursor.close();

inputStream.close();

outputStream.close();

return file.getPath();

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

附上全系统的代码:

/**

* 文件Uri转路径(兼容各品牌手机)

*/

public class PathUtils {

/**

* android7.0以上处理方法

*/

private static String getFilePathForN(Context context, Uri uri) {

try {

Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null);

int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);

returnCursor.moveToFirst();

String name = (returnCursor.getString(nameIndex));

File file = new File(context.getFilesDir(), name);

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 = Math.min(bytesAvailable, maxBufferSize);

final byte[] buffers = new byte[bufferSize];

while ((read = inputStream.read(buffers)) != -1) {

outputStream.write(buffers, 0, read);

}

returnCursor.close();

inputStream.close();

outputStream.close();

return file.getPath();

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* 全平台处理方法

*/

public static String getPath(final Context context, final Uri uri) throws Exception {

final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

final boolean isN = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;

if (isN) {

return getFilePathForN(context, uri);

}

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

}

}

// DownloadsProvider

else if (isDownloadsDocument(uri)) {

final String id = DocumentsContract.getDocumentId(uri);

final Uri contentUri = ContentUris.withAppendedId(

Uri.parse("content://downloads/public_downloads"), StringUtils.toLong(id));

return getDataColumn(context, contentUri, null, null);

}

// MediaProvider

else if (isMediaDocument(uri)) {

final String docId = DocumentsContract.getDocumentId(uri);

final String[] split = docId.split(":");

final String type = split[0];

Uri contentUri = null;

if ("image".equals(type)) {

contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

} else if ("video".equals(type)) {

contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;

} else if ("audio".equals(type)) {

contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;

}

final String selection = "_id=?";

final String[] selectionArgs = new String[] {

split[1]

};

return getDataColumn(context, contentUri, selection, selectionArgs);

}

}

// MediaStore (and general)

else if ("content".equalsIgnoreCase(uri.getScheme())) {

return getDataColumn(context, uri, null, null);

}

// File

else if ("file".equalsIgnoreCase(uri.getScheme())) {

return uri.getPath();

}

return null;

}

/**

* 获取此Uri的数据列的值。这对于MediaStore uri和其他基于文件的内容提供程序非常有用。

*/

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()) {

final int column_index = cursor.getColumnIndexOrThrow(column);

return cursor.getString(column_index);

}

} catch (IllegalArgumentException e){

//do nothing

} finally {

if (cursor != null)

cursor.close();

}

return null;

}

public static boolean isExternalStorageDocument(Uri uri) {

return "com.android.externalstorage.documents".equals(uri.getAuthority());

}

public static boolean isDownloadsDocument(Uri uri) {

return "com.android.providers.downloads.documents".equals(uri.getAuthority());

}

public static boolean isMediaDocument(Uri uri) {

return "com.android.providers.media.documents".equals(uri.getAuthority());

}

}

另发现一篇,亲测,Android 4.4到Android 10可用,测试的系统有VIVO、OPPO、MIUI、EMUI...

解决的国内产商问题:华为的黄色图标管理器,他返回了4.4的标准的Uri了,不是4.4以上的标准的Uri,导致解析的时候,判断到版本 > 4.4,然后用了4.4以上的标准的解析,然后失败了,并非不回调。

直接可用的代码片段:

public class FileUtils {

private Context context;

public FileUtils(Context context) {

this.context = context;

}

public String getFilePathByUri(Uri uri) {

// 以 file:// 开头的

if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {

return uri.getPath();

}

// 以/storage开头的也直接返回

if (isOtherDocument(uri)) {

return uri.getPath();

}

// 版本兼容的获取!

String path = getFilePathByUri_BELOWAPI11(uri);

if (path != null) {

LogUtils.d("getFilePathByUri_BELOWAPI11获取到的路径为:" + path);

return path;

}

path = getFilePathByUri_API11to18(uri);

if (path != null) {

LogUtils.d("getFilePathByUri_API11to18获取到的路径为:" + path);

return path;

}

path = getFilePathByUri_API19(uri);

LogUtils.d("getFilePathByUri_API19获取到的路径为:" + path);

return path;

}

private String getFilePathByUri_BELOWAPI11(Uri uri) {

// 以 content:// 开头的,比如 content://media/extenral/images/media/17766

if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {

String path = null;

String[] projection = new String[]{MediaStore.Images.Media.DATA};

Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);

if (cursor != null) {

if (cursor.moveToFirst()) {

int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

if (columnIndex > -1) {

path = cursor.getString(columnIndex);

}

}

cursor.close();

}

return path;

}

return null;

}

private String getFilePathByUri_API11to18(Uri contentUri) {

String[] projection = {MediaStore.Images.Media.DATA};

String result = null;

CursorLoader cursorLoader = new CursorLoader(context, contentUri, projection, 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);

cursor.close();

}

return result;

}

private String getFilePathByUri_API19(Uri uri) {

// 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700

if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

if (DocumentsContract.isDocumentUri(context, uri)) {

if (isExternalStorageDocument(uri)) {

// ExternalStorageProvider

String docId = DocumentsContract.getDocumentId(uri);

String[] split = docId.split(":");

String type = split[0];

if ("primary".equalsIgnoreCase(type)) {

if (split.length > 1) {

return Environment.getExternalStorageDirectory() + "/" + split[1];

} else {

return Environment.getExternalStorageDirectory() + "/";

}

// This is for checking SD Card

}

} else if (isDownloadsDocument(uri)) {

//下载内容提供者时应当判断下载管理器是否被禁用

int stateCode = context.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads");

if (stateCode != 0 && stateCode != 1) {

return null;

}

String id = DocumentsContract.getDocumentId(uri);

// 如果出现这个RAW地址,我们则可以直接返回!

if (id.startsWith("raw:")) {

return id.replaceFirst("raw:", "");

}

if (id.contains(":")) {

String[] tmp = id.split(":");

if (tmp.length > 1) {

id = tmp[1];

}

}

Uri contentUri = Uri.parse("content://downloads/public_downloads");

LogUtils.d("测试打印Uri: " + uri);

try {

contentUri = ContentUris.withAppendedId(contentUri, Long.parseLong(id));

} catch (Exception e) {

e.printStackTrace();

}

String path = getDataColumn(contentUri, null, null);

if (path != null) return path;

// 兼容某些特殊情况下的文件管理器!

String fileName = getFileNameByUri(uri);

if (fileName != null) {

path = Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;

return path;

}

} else if (isMediaDocument(uri)) {

// MediaProvider

String docId = DocumentsContract.getDocumentId(uri);

String[] split = docId.split(":");

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;

}

String selection = "_id=?";

String[] selectionArgs = new String[]{split[1]};

return getDataColumn(contentUri, selection, selectionArgs);

}

}

}

return null;

}

private String getFileNameByUri(Uri uri) {

String relativePath = getFileRelativePathByUri_API18(uri);

if (relativePath == null) relativePath = "";

final String[] projection = {

MediaStore.MediaColumns.DISPLAY_NAME

};

try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null)) {

if (cursor != null && cursor.moveToFirst()) {

int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME);

return relativePath + cursor.getString(index);

}

}

return null;

}

private String getFileRelativePathByUri_API18(Uri uri) {

final String[] projection;

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {

projection = new String[]{

MediaStore.MediaColumns.RELATIVE_PATH

};

try (Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null)) {

if (cursor != null && cursor.moveToFirst()) {

int index = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.RELATIVE_PATH);

return cursor.getString(index);

}

}

}

return null;

}

private String getDataColumn(Uri uri, String selection, String[] selectionArgs) {

final String column = MediaStore.Images.Media.DATA;

final String[] projection = {column};

try (Cursor 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);

}

} catch (IllegalArgumentException iae) {

iae.printStackTrace();

}

return null;

}

private boolean isExternalStorageDocument(Uri uri) {

return "com.android.externalstorage.documents".equals(uri.getAuthority());

}

private boolean isOtherDocument(Uri uri) {

// 以/storage开头的也直接返回

if (uri != null && uri.getPath() != null) {

String path = uri.getPath();

if (path.startsWith("/storage")) {

return true;

}

if (path.startsWith("/external_files")) {

return true;

}

}

return false;

}

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

}

}

调用 getFilePathByUri(Uri uri) 即可获得最终的路径。

到此这篇关于Android7.0以上Uri转路径的方法实现(已验证)的文章就介绍到这了,更多相关Android7 Uri转路径内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值