Android7.0以上Uri轉(zhuǎn)路徑的方法實(shí)現(xiàn)(已驗(yàn)證)
網(wǎng)絡(luò)上看到過(guò)很多種Uri轉(zhuǎn)路徑的方法,可基本上都只適用于很少的Uri值,可能沒(méi)有結(jié)果(例如,對(duì)于由MediaStore索引的非本地文件),也可能沒(méi)有可用的結(jié)果(例如,對(duì)于可移動(dòng)存儲(chǔ)上的文件)。
解決方法
使用ContentResolver和openInputStream()在Uri標(biāo)識(shí)的內(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;
}
/**
* 全平臺(tái)處理方法
*/
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ù)列的值。這對(duì)于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)一篇,親測(cè),Android 4.4到Android 10可用,測(cè)試的系統(tǒng)有VIVO、OPPO、MIUI、EMUI...
解決的國(guó)內(nèi)產(chǎn)商問(wèn)題:華為的黃色圖標(biāo)管理器,他返回了4.4的標(biāo)準(zhǔn)的Uri了,不是4.4以上的標(biāo)準(zhǔn)的Uri,導(dǎo)致解析的時(shí)候,判斷到版本 > 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)容提供者時(shí)應(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)這個(gè)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("測(cè)試打印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)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android開發(fā)調(diào)用WebService的方法示例
這篇文章主要介紹了Android開發(fā)調(diào)用WebService的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Android調(diào)用WebService的原理、實(shí)現(xiàn)方法與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2017-10-10
Android開發(fā)實(shí)現(xiàn)的保存圖片到相冊(cè)功能示例
這篇文章主要介紹了Android開發(fā)實(shí)現(xiàn)的保存圖片到相冊(cè)功能,結(jié)合實(shí)例形式分析了Android圖片命名、保存、權(quán)限控制等相關(guān)操作技巧,需要的朋友可以參考下2019-03-03
Android實(shí)現(xiàn)桌面懸浮窗、蒙板效果實(shí)例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)桌面懸浮窗、蒙板效果實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2016-05-05
Android ListView的OnItemClickListener詳解
這篇文章主要介紹了Android ListView的OnItemClickListener詳解的相關(guān)資料,涉及到OnItemClickListener的position和id參數(shù)做詳細(xì)的解釋的知識(shí)點(diǎn),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2016-07-07
Android網(wǎng)頁(yè)H5 Input選擇相機(jī)和系統(tǒng)相冊(cè)
這篇文章主要為大家詳細(xì)介紹了Android網(wǎng)頁(yè)H5 Input選擇相機(jī)和系統(tǒng)相冊(cè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10
Android刮刮卡實(shí)現(xiàn)原理與代碼講解
這篇文章主要為大家詳細(xì)介紹了Android刮刮卡實(shí)現(xiàn)原理、實(shí)現(xiàn)原理步驟以及代碼講解,感興趣的小伙伴們可以參考一下2016-04-04
Android 快速實(shí)現(xiàn)防止網(wǎng)絡(luò)重復(fù)請(qǐng)求&按鈕重復(fù)點(diǎn)擊的方法
下面小編就為大家分享一篇Android 快速實(shí)現(xiàn)防止網(wǎng)絡(luò)重復(fù)請(qǐng)求&按鈕重復(fù)點(diǎn)擊的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
Android實(shí)現(xiàn)圖片自動(dòng)輪播并且支持手勢(shì)左右無(wú)限滑動(dòng)
這篇文章給大家介紹android實(shí)現(xiàn)圖片自動(dòng)輪播并且支持手勢(shì)左右無(wú)限滑動(dòng),代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-10-10
Android System fastboot 介紹和使用教程
Fastboot是Android快速升級(jí)的一種方法,Fastboot的協(xié)議fastboot_protocol.txt在源碼目錄./bootable/bootloader/legacy下可以找到,本文給大家介紹Android System fastboot 介紹和使用教程,感興趣的朋友一起看看吧2024-01-01

