Android7.0以上Uri轉(zhuǎn)路徑的方法實(shí)現(xiàn)(已驗(yàn)證)
網(wǎng)絡(luò)上看到過很多種Uri轉(zhuǎn)路徑的方法,可基本上都只適用于很少的Uri值,可能沒有結(jié)果(例如,對于由MediaStore索引的非本地文件),也可能沒有可用的結(jié)果(例如,對于可移動存儲上的文件)。
解決方法
使用ContentResolver和openInputStream()在Uri標(biāo)識的內(nèi)容上獲取InputStream。在控制的文件上使用InputStream和FileOutputStream復(fù)制內(nèi)容,然后使用該文件。
代碼如下:
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; }
附上全系統(tǒng)的代碼:
/** * 文件Uri轉(zhuǎn)路徑(兼容各品牌手機(jī)) */ 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的數(shù)據(jù)列的值。這對于MediaStore uri和其他基于文件的內(nèi)容提供程序非常有用。 */ 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()); } }
參考資料:https://stackoverflow.com/questions/42508383/illegalargumentexception-column-data-does-not-exist
另發(fā)現(xiàn)一篇,親測,Android 4.4到Android 10可用,測試的系統(tǒng)有VIVO、OPPO、MIUI、EMUI...
解決的國內(nèi)產(chǎn)商問題:華為的黃色圖標(biāo)管理器,他返回了4.4的標(biāo)準(zhǔn)的Uri了,不是4.4以上的標(biāo)準(zhǔn)的Uri,導(dǎo)致解析的時候,判斷到版本 > 4.4,然后用了4.4以上的標(biāo)準(zhǔn)的解析,然后失敗了,并非不回調(diào)。
直接可用的代碼片段:
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)) { //下載內(nèi)容提供者時應(yīng)當(dāng)判斷下載管理器是否被禁用 int stateCode = context.getPackageManager().getApplicationEnabledSetting("com.android.providers.downloads"); if (stateCode != 0 && stateCode != 1) { return null; } String id = DocumentsContract.getDocumentId(uri); // 如果出現(xiàn)這個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()); } }
調(diào)用 getFilePathByUri(Uri uri) 即可獲得最終的路徑。
到此這篇關(guān)于Android7.0以上Uri轉(zhuǎn)路徑的方法實(shí)現(xiàn)(已驗(yàn)證)的文章就介紹到這了,更多相關(guān)Android7 Uri轉(zhuǎn)路徑內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
完美解決Android Studio集成crashlytics后無法編譯的問題
下面小編就為大家?guī)硪黄昝澜鉀QAndroid Studio集成crashlytics后無法編譯的問題。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Android自定義View繪圖實(shí)現(xiàn)拖影動畫
這篇文章主要介紹了Android自定義View繪圖實(shí)現(xiàn)拖影動畫,,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09基于Android studio3.6的JNI教程之ncnn之語義分割ENet
這篇文章主要介紹了基于Android studio3.6的JNI教程之ncnn之語義分割ENet的相關(guān)知識,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值 ,需要的朋友可以參考下2020-03-03Android編程向服務(wù)器發(fā)送請求時出現(xiàn)中文亂碼問題的解決方法
這篇文章主要介紹了Android編程向服務(wù)器發(fā)送請求時出現(xiàn)中文亂碼問題的解決方法,實(shí)例分析了Android參數(shù)傳遞過程中中文亂碼的解決技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-11-11RxJava 1升級到RxJava 2過程中踩過的一些“坑”
RxJava2相比RxJava1,它的改動還是很大的,那么下面這篇文章主要給大家總結(jié)了在RxJava 1升級到RxJava 2過程中踩過的一些“坑”,文中介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下來要一起看看吧。2017-05-05Android使用Kotlin和RxJava 2.×實(shí)現(xiàn)短信驗(yàn)證碼倒計時效果
本篇文章主要介紹了Android使用Kotlin和RxJava 2.×實(shí)現(xiàn)短信驗(yàn)證碼倒計時效果,非常具有實(shí)用價值,需要的朋友可以參考下2017-12-12android使用handlerthread創(chuàng)建線程示例
這篇文章主要介紹了android使用handlerthread創(chuàng)建線程,講解了這種方式的好處及為什么不使用Thread類的原因2014-01-01