如何从 Uri xamarin android 获取实际路径
Posted
技术标签:
【中文标题】如何从 Uri xamarin android 获取实际路径【英文标题】:How to get actual path from Uri xamarin android 【发布时间】:2018-04-09 02:10:35 【问题描述】:我想从 Uri 获取实际路径,当我使用意图选择文件时,它将返回 URI,但下面的代码行不适用于将 URI 转换为字符串路径
打开文件选择器
public static void PickFile(Activity context)
Intent intent = new Intent(Intent.ActionGetContent);
intent.SetType("*/*");
context.StartActivityForResult(intent, PICKFILE_RESULT_CODE);
获取结果
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
if(requestCode == PICKFILE_RESULT_CODE)
string filepath = GetRealPathFromURI(MainActivity.act, data.Data);
public static string GetRealPathFromURI(Activity act, android.Net.Uri contentURI)
try
ICursor cursor = act.ContentResolver.Query(contentURI, null, null, null, null);
cursor.MoveToFirst();
string documentId = cursor.GetString(0);
documentId = documentId.Split(':')[1];
cursor.Close();
cursor = act.ContentResolver.Query(
MediaStore.Images.Media.ExternalContentUri,
null, MediaStore.Images.Media.InterfaceConsts.Id + " = ? ", new[] documentId , null);
cursor.MoveToFirst();
string path = cursor.GetString(cursor.GetColumnIndex(MediaStore.Images.Media.InterfaceConsts.Data));
cursor.Close();
return path;
catch(Exception e)
Log.Debug("TAG_DATA",e.ToString());
return "";
错误
System.IndexOutOfRangeException:索引超出范围 数组。
谁能给我解释一下,我在这段代码中做错了什么。
【问题讨论】:
"I want to get actual path from Uri,"
不,你不需要,你需要一个InputStream
,阅读ContentResolver
API 文档了解如何做到这一点
好的,那我想知道文件名和类型怎么办?
参见 ContentResolver
和 android.provider.OpenableColumns
文档
【参考方案1】:
如何从 Uri xamarin android 获取实际路径
我注意到您正在使用MediaStore.Images.Media.ExternalContentUri
属性,您想要的是获取图库图像的真实路径?
如果你想实现这个功能,你可以阅读我的回答:Get Path of Gallery Image ?
private string GetRealPathFromURI(Android.Net.Uri uri)
string doc_id = "";
using (var c1 = ContentResolver.Query(uri, null, null, null, null))
c1.MoveToFirst();
string document_id = c1.GetString(0);
doc_id = document_id.Substring(document_id.LastIndexOf(":") + 1);
string path = null;
// The projection contains the columns we want to return in our query.
string selection = Android.Provider.MediaStore.Images.Media.InterfaceConsts.Id + " =? ";
using (var cursor = ContentResolver.Query(Android.Provider.MediaStore.Images.Media.ExternalContentUri, null, selection, new string[] doc_id , null))
if (cursor == null) return path;
var columnIndex = cursor.GetColumnIndexOrThrow(Android.Provider.MediaStore.Images.Media.InterfaceConsts.Data);
cursor.MoveToFirst();
path = cursor.GetString(columnIndex);
return path;
更新:
这是一个解决方案:
private string GetActualPathFromFile(Android.Net.Uri uri)
bool isKitKat = Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat;
if (isKitKat && DocumentsContract.IsDocumentUri(this, uri))
// ExternalStorageProvider
if (isExternalStorageDocument(uri))
string docId = DocumentsContract.GetDocumentId(uri);
char[] chars = ':' ;
string[] split = docId.Split(chars);
string type = split[0];
if ("primary".Equals(type, StringComparison.OrdinalIgnoreCase))
return Android.OS.Environment.ExternalStorageDirectory + "/" + split[1];
// DownloadsProvider
else if (isDownloadsDocument(uri))
string id = DocumentsContract.GetDocumentId(uri);
Android.Net.Uri contentUri = ContentUris.WithAppendedId(
Android.Net.Uri.Parse("content://downloads/public_downloads"), long.Parse(id));
//System.Diagnostics.Debug.WriteLine(contentUri.ToString());
return getDataColumn(this, contentUri, null, null);
// MediaProvider
else if (isMediaDocument(uri))
String docId = DocumentsContract.GetDocumentId(uri);
char[] chars = ':' ;
String[] split = docId.Split(chars);
String type = split[0];
Android.Net.Uri contentUri = null;
if ("image".Equals(type))
contentUri = MediaStore.Images.Media.ExternalContentUri;
else if ("video".Equals(type))
contentUri = MediaStore.Video.Media.ExternalContentUri;
else if ("audio".Equals(type))
contentUri = MediaStore.Audio.Media.ExternalContentUri;
String selection = "_id=?";
String[] selectionArgs = new String[]
split[1]
;
return getDataColumn(this, contentUri, selection, selectionArgs);
// MediaStore (and general)
else if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.LastPathSegment;
return getDataColumn(this, uri, null, null);
// File
else if ("file".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
return uri.Path;
return null;
public static String getDataColumn(Context context, Android.Net.Uri uri, String selection, String[] selectionArgs)
ICursor cursor = null;
String column = "_data";
String[] projection =
column
;
try
cursor = context.ContentResolver.Query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.MoveToFirst())
int index = cursor.GetColumnIndexOrThrow(column);
return cursor.GetString(index);
finally
if (cursor != null)
cursor.Close();
return null;
//Whether the Uri authority is ExternalStorageProvider.
public static bool isExternalStorageDocument(Android.Net.Uri uri)
return "com.android.externalstorage.documents".Equals(uri.Authority);
//Whether the Uri authority is DownloadsProvider.
public static bool isDownloadsDocument(Android.Net.Uri uri)
return "com.android.providers.downloads.documents".Equals(uri.Authority);
//Whether the Uri authority is MediaProvider.
public static bool isMediaDocument(Android.Net.Uri uri)
return "com.android.providers.media.documents".Equals(uri.Authority);
//Whether the Uri authority is Google Photos.
public static bool isGooglePhotosUri(Android.Net.Uri uri)
return "com.google.android.apps.photos.content".Equals(uri.Authority);
获取OnActivityResult
中的实际路径:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 0)
var uri = data.Data;
string path = GetActualPathFromFile(uri);
System.Diagnostics.Debug.WriteLine("Image path == " + path);
【讨论】:
ContentResolver.Query 在 VS2017 中显示以下错误非静态字段需要对象引用。可以改用:Application.Context.ContentResolver.Query 很好,但在 Android 9.0 上失败了。【参考方案2】:阅读这篇文章,你会得到非常简单的答案。
https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html
我的代码如下:
var targetPath = $"FileDirPath.DirectorySeparatorCharFileName";
const int bufferSize = 1024;
using (var inputStream = Activity.ContentResolver.OpenInputStream(audioFileUri))
using (var outputStream = File.Create(targetPath))
var buffer = new byte[bufferSize];
while (true)
var count = inputStream.Read(buffer, 0, bufferSize);
if (count > 0)
outputStream.Write(buffer, 0, count);
if (count < bufferSize) break;
【讨论】:
这似乎是一致地获取文件内容的唯一方法。是的,它涉及复制文件,这很可惜,但在很多情况下,其他解决方案都不起作用。【参考方案3】:这是 Xamarin.Android API 21+ 的完整解决方案: https://gist.github.com/IlyaLavrov97/a76063a49515764b60d5fba3ebbb2662
它包括来自私人下载文件夹和谷歌磁盘的文件更新。
解决方案的主要部分如下:
/// <summary>
/// Main feature. Return actual path for file from uri.
/// </summary>
/// <param name="uri">File's uri</param>
/// <param name="context">Current context</param>
/// <returns>Actual path</returns>
private static string GetActualPathForFile(global::Android.Net.Uri uri, Context context)
bool isKitKat = Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat;
if (isKitKat && DocumentsContract.IsDocumentUri(context, uri))
// ExternalStorageProvider
if (IsExternalStorageDocument(uri))
string docId = DocumentsContract.GetDocumentId(uri);
char[] chars = ':' ;
string[] split = docId.Split(chars);
string type = split[0];
if ("primary".Equals(type, StringComparison.OrdinalIgnoreCase))
return global::Android.OS.Environment.ExternalStorageDirectory + "/" + split[1];
// Google Drive
else if (IsDiskContentUri(uri))
return GetDriveFileAbsolutePath(context, uri);
// DownloadsProvider
else if (IsDownloadsDocument(uri))
try
string id = DocumentsContract.GetDocumentId(uri);
if (!TextUtils.IsEmpty(id))
if (id.StartsWith("raw:"))
return id.Replace("raw:", "");
string[] contentUriPrefixesToTry = new string[]
"content://downloads/public_downloads",
"content://downloads/my_downloads",
"content://downloads/all_downloads"
;
string path = null;
foreach (string contentUriPrefix in contentUriPrefixesToTry)
global::Android.Net.Uri contentUri = ContentUris.WithAppendedId(
global::Android.Net.Uri.Parse(contentUriPrefix), long.Parse(id));
path = GetDataColumn(context, contentUri, null, null);
if (!string.IsNullOrEmpty(path))
return path;
// path could not be retrieved using ContentResolver, therefore copy file to accessible cache using streams
string fileName = GetFileName(context, uri);
Java.IO.File cacheDir = GetDocumentCacheDir(context);
Java.IO.File file = GenerateFileName(fileName, cacheDir);
if (file != null)
path = file.AbsolutePath;
SaveFileFromUri(context, uri, path);
// last try
if (string.IsNullOrEmpty(path))
return global::Android.OS.Environment.ExternalStorageDirectory.ToString() + "/Download/" + GetFileName(context, uri);
return path;
catch
return global::Android.OS.Environment.ExternalStorageDirectory.ToString() + "/Download/" + GetFileName(context, uri);
// MediaProvider
else if (IsMediaDocument(uri))
string docId = DocumentsContract.GetDocumentId(uri);
char[] chars = ':' ;
string[] split = docId.Split(chars);
string type = split[0];
global::Android.Net.Uri contentUri = null;
if ("image".Equals(type))
contentUri = MediaStore.Images.Media.ExternalContentUri;
else if ("video".Equals(type))
contentUri = MediaStore.Video.Media.ExternalContentUri;
else if ("audio".Equals(type))
contentUri = MediaStore.Audio.Media.ExternalContentUri;
string selection = "_id=?";
string[] selectionArgs = new string[] split[1] ;
return GetDataColumn(context, contentUri, selection, selectionArgs);
// MediaStore (and general)
else if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
// Return the remote address
if (IsGooglePhotosUri(uri))
return uri.LastPathSegment;
// Google Disk document .legacy
if (IsDiskLegacyContentUri(uri))
return GetDriveFileAbsolutePath(context, uri);
return GetDataColumn(context, uri, null, null);
// File
else if ("file".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
return uri.Path;
return null;
【讨论】:
【参考方案4】:简而言之,这很有帮助。
public static string GetRealPathFromURI(Activity act, Android.Net.Uri contentURI)
try
Android.Database.ICursor cursor = act.ContentResolver.Query(contentURI, null, null, null, null);
cursor.MoveToFirst();
string documentId = cursor.GetString(0);
documentId = documentId.Split(':')[1];
cursor.Close();
cursor = act.ContentResolver.Query(
MediaStore.Images.Media.ExternalContentUri,
null, MediaStore.Images.Media.InterfaceConsts.Id + " = ? ", new[] documentId , null);
cursor.MoveToFirst();
string path = cursor.GetString(cursor.GetColumnIndex(MediaStore.Images.Media.InterfaceConsts.Data));
cursor.Close();
return path;
catch (Exception e)
Console.WriteLine(e.ToString());
return "";
【讨论】:
以上是关于如何从 Uri xamarin android 获取实际路径的主要内容,如果未能解决你的问题,请参考以下文章
错误 400:分发 Xamarin Forms 应用程序时的 redirect_uri_mismatch
如何从列表视图中从 onItemClick() 获得的列表项的联系人 Uri 中检索数据?
Uri 图像未显示在 android 设备上 (Xamarin)
如果我有内容 URI 而不是路径,是不是可以在 Android 中使用 Xamarin.Essentials.Launcher 启动文件?