2

I have button to attach a file

private void ImgAttach_Click(object sender, EventArgs e)
        {
            var _Intent = new Intent();
            _Intent.SetType("*/*");
            _Intent.SetAction(Intent.ActionGetContent);
            StartActivityForResult(Intent.CreateChooser(_Intent, "Select File"), 0);
        }

After selecting a file, I can get the real path if it is an image. If it is not an image (PDF, DOCX), it does not work! For example, here is the path returned after selecting an image:

"/storage/0403-0201/Pictures/beach_huts.jpg"

but after selecting a file, this error appears

Android.Database.CursorIndexOutOfBoundsException: Index 0 requested, with a size of 0

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
            base.OnActivityResult(requestCode, resultCode, data);

            if (resultCode == Result.Ok)
            {
                Android.Net.Uri selectedUri = data.Data;
                ////////////////////////
                string s = GetRealPathFromURI(selectedUri);
            }
}

private string GetRealPathFromURI(Android.Net.Uri contentURI)
        {
            Android.Database.ICursor cursor = ContentResolver.Query(contentURI, null, null, null, null);
            cursor.MoveToFirst();
            string documentId = cursor.GetString(0);
            documentId = documentId.Split(':')[1];
            cursor.Close();

            cursor = ContentResolver.Query(
            Android.Provider.MediaStore.Images.Media.ExternalContentUri,
            null, Android.Provider.MediaStore.Images.Media.InterfaceConsts.Id + " = ? ", new[] { documentId }, null);
            cursor.MoveToFirst();
            string path = cursor.GetString(cursor.GetColumnIndex(Android.Provider.MediaStore.Images.ImageColumns.Data));
            cursor.Close();

            return path;
        }
ashley
  • 576
  • 3
  • 22

2 Answers2

2

This is the Answer

/**
 * 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.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @author paulburke
 */
        public static string GetRealPathFromURI(Context context,Android.Net.Uri uri)
        {

            bool isKitKat = Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat;

            // DocumentProvider
            if (isKitKat && Android.Provider.DocumentsContract.IsDocumentUri(context, uri))
            {
                // ExternalStorageProvider
                if (isExternalStorageDocument(uri))
                {
                    string docId = Android.Provider.DocumentsContract.GetDocumentId(uri);
                    string[] split = docId.Split(':');
                    string type = split[0];

                    if ("primary".Equals(type,StringComparison.OrdinalIgnoreCase))
                    {
                        return Android.OS.Environment.ExternalStorageDirectory + "/" + split[1];
                    }

                    // TODO handle non-primary volumes
                }
                // DownloadsProvider
                else if (isDownloadsDocument(uri))
                {

                    string id = Android.Provider.DocumentsContract.GetDocumentId(uri);
                    Android.Net.Uri contentUri = ContentUris.WithAppendedId(Android.Net.Uri.Parse("content://downloads/public_downloads"), Convert.ToInt64(id));

                    return getDataColumn(context, contentUri, null, null);
                }
                // MediaProvider
                else if (isMediaDocument(uri))
                {
                    string docId = Android.Provider.DocumentsContract.GetDocumentId(uri);
                    string[] split = docId.Split(':');
                    string type = split[0];

                    Android.Net.Uri contentUri = null;
                    if ("image".Equals(type))
                    {
                        contentUri = Android.Provider.MediaStore.Images.Media.ExternalContentUri;
                    }
                    else if ("video".Equals(type))
                    {
                        contentUri = Android.Provider.MediaStore.Video.Media.ExternalContentUri;
                    }
                    else if ("audio".Equals(type))
                    {
                        contentUri = Android.Provider.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 getDataColumn(context, uri, null, null);
            }
            // File
            else if ("file".Equals(uri.Scheme,StringComparison.OrdinalIgnoreCase))
            {
                return uri.Path;
            }

            return null;
        }

        /**
         * 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.
         */
        public static String getDataColumn(Context context,Android.Net.Uri uri, String selection,
                String[] selectionArgs)
        {

            Android.Database.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 column_index = cursor.GetColumnIndexOrThrow(column);
                    return cursor.GetString(column_index);
                }
            }
            finally
            {
                if (cursor != null)
                    cursor.Close();
            }
            return null;
        }


        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is ExternalStorageProvider.
         */
        public static bool isExternalStorageDocument(Android.Net.Uri uri)
        {
            return "com.android.externalstorage.documents".Equals(uri.Authority);
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is DownloadsProvider.
         */
        public static bool isDownloadsDocument(Android.Net.Uri uri)
        {
            return "com.android.providers.downloads.documents".Equals(uri.Authority);
        }

        /**
         * @param uri The Uri to check.
         * @return Whether the Uri authority is MediaProvider.
         */
        public static bool isMediaDocument(Android.Net.Uri uri)
        {
            return "com.android.providers.media.documents".Equals(uri.Authority);
        }
1

Normally you would want to check the mime-type of the item selected, but this is a quick hack of handling items that are not in the media store:

string GetRealPathFromURI(Android.Net.Uri contentURI)
{
    try
    {
        Android.Database.ICursor cursor = ContentResolver.Query(contentURI, null, null, null, null);
        if (cursor != null)
        {
            cursor.MoveToFirst();
            string documentId = cursor.GetString(0);
            documentId = documentId.Split(':')[1];
            cursor.Close();
            cursor = ContentResolver.Query(Android.Provider.MediaStore.Images.Media.ExternalContentUri,
                                           null,
                                           Android.Provider.MediaStore.Images.Media.InterfaceConsts.Id + " = ? ", new[] { documentId },
                                           null);
            cursor.MoveToFirst();
            string path = cursor.GetString(cursor.GetColumnIndex(Android.Provider.MediaStore.Images.ImageColumns.Data));
            cursor.Close();
            return path;
        }
        return contentURI.Path;
    }
    catch (Android.Database.CursorIndexOutOfBoundsException ex)
    {
        return contentURI.Path;
    }
}
SushiHangover
  • 73,120
  • 10
  • 106
  • 165
  • Thank, but this error occured when i try to convert it to byte[] .... System.IO.DirectoryNotFoundException: Could not find a part of the path "/document/primary:Test.pdf". – Ahmed Abd Elhalem Nov 02 '16 at 17:15
  • @AhmedAbdElhalem You are dealing with `document primary:` locations, you will need to use `DocumentContracts` and check SDK levels, here is a good starting point to show how you deal with `DocumentsContract`, downloaded files, and mediastore-based files via `MediaProvider`, it is in Java but it converts quickly to C#: http://stackoverflow.com/a/20559175/4984832 – SushiHangover Nov 02 '16 at 17:27