Action.Picker 返回无效/错误的 Uri(如何从多个选取的图库 img 中获取路径或字节 [])

Posted

技术标签:

【中文标题】Action.Picker 返回无效/错误的 Uri(如何从多个选取的图库 img 中获取路径或字节 [])【英文标题】:Action.Picker returns invalid/wrong Uri (How to get path or byte[] from multiple picked gallery img) 【发布时间】:2019-02-15 18:20:57 【问题描述】:

我有一个表单应用程序,我需要从手机存储中选择“一对多”图像。 为此,我使用依赖注入系统。

我的问题是我得到一个 android.netUri 的地方,它解析为一个不存在的文件......以及一个我以前从未见过的文件名。 更重要的是,如果我选择在过去几个小时内拍摄的照片,这个代码就可以工作......

我已经走到了尽头,我真的希望有人能指出我做错了什么。

我用以下方式启动 Picker 活动:

[assembly: Dependency(typeof(ImagePickerService))]
namespace MyApp.Droid

    public class ImagePickerService : Java.Lang.Object, IImagePickerService
    
        public async Task OpenGallery()
        
            try
            
                var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage);
                if (status != PermissionStatus.Granted)
                
                    if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Storage))
                    
                        Toast.MakeText(CrossCurrentActivity.Current.Activity, "Need Storage permission to access to your photos.", ToastLength.Long).Show();
                    

                    var results = await CrossPermissions.Current.RequestPermissionsAsync(new[]  Permission.Storage );
                    status = results[Permission.Storage];
                

                if (status == PermissionStatus.Granted)
                
                    Toast.MakeText(CrossCurrentActivity.Current.Activity, "Pick max 20 images", ToastLength.Long).Show();
                    var imageIntent = new Intent(Intent.ActionPick);
                    imageIntent.SetType("image/*");
                    imageIntent.PutExtra(Intent.ExtraAllowMultiple, true);
                    imageIntent.SetAction(Intent.ActionPick);
                    CrossCurrentActivity.Current.Activity.StartActivityForResult(Intent.CreateChooser(imageIntent, "Pick pictures"), 100);

                
                else if (status != PermissionStatus.Unknown)
                
                    Toast.MakeText(CrossCurrentActivity.Current.Activity, "Permission Denied. Can not continue, try again.", ToastLength.Long).Show();
                
            
            catch (Exception ex)
            
                Console.WriteLine(ex.ToString());
                Toast.MakeText(CrossCurrentActivity.Current.Activity, "Error. Can not continue, try again.", ToastLength.Long).Show();
            
        

    

然后在我的 MainActivity.cs 我有 OnActivityResult 我尝试使用 ContentResolver.OpenInputStream 来获取图像字节,但没有成功,因此将其注释掉 atm。

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

            if (requestCode == OPENGALLERYCODE && resultCode == Result.Ok)
            
                List<string> images = new List<string>();

                if (data != null)
                
                    ClipData clipData = data.ClipData;
                    if (clipData != null)
                    
                        for (int i = 0; i < clipData.ItemCount; i++)
                        
                            ClipData.Item item = clipData.GetItemAt(i);
                            /*
                            var stream = ContentResolver.OpenInputStream(item.Uri); //This throws "FileNotFound"
                            byte[] byteArray;
                            using (var memoryStream = new MemoryStream())
                            
                                stream.CopyTo(memoryStream);
                                byteArray = memoryStream.ToArray();
                                stream.Close();
                                stream = null;
                            
                            stream = ContentResolver.OpenInputStream(item.Uri);
                            var exif = new ExifInterface(stream);
                            stream.Close();
                            */

                            Android.Net.Uri uri = item.Uri;
                            var path = GetActualPathFromFile(uri);

                            if (path != null)
                            
                                var tmpImgPath = RotateToOriginalDimention(path);
                                images.Add(tmpImgPath);
                            
                        
                    
                    else
                    
                        Android.Net.Uri uri = data.Data;
                        var path = GetActualPathFromFile(uri);

                        if (path != null)
                        
                            var tmpImgPath = RotateToOriginalDimention(path);
                            images.Add(tmpImgPath);
                        
                    
                    MessagingCenter.Send<App, List<string>>((App)Xamarin.Forms.Application.Current, "ImagesSelected", images);
                
            
        

还有 GetActualPathFromFile(也在我的 MainActivity.cs 中) 孔函数在下面,但我点击了这部分代码并进入“FileNotFound”

(...)
                else if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
                

                    var retval2 = getDataColumn(this, uri, null, null);
                    if (File.Exists(retval2)) //<----------------------- This returns "false"
                    
                        return retval2;
                    
                    else
                    
                        throw new Exception("file not found " + retval2);
                    
                
(...)

GetActualPathFromFile 漏洞

        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))
                    
                        var retval = Android.OS.Environment.ExternalStorageDirectory + "/" + split[1];
                        if (File.Exists(retval))
                        
                            return retval;
                        
                        else
                        
                            throw new Exception("file not found " + retval);
                        
                    
                
                // 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());

                    var retval = getDataColumn(this, contentUri, null, null);
                    if (File.Exists(retval))
                    
                        return retval;
                    
                    else
                    
                        throw new Exception("file not found " + retval);
                    
                
                // 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]
                    ;

                    var retval = getDataColumn(this, contentUri, selection, selectionArgs);
                    if (File.Exists(retval))
                    
                        return retval;
                    
                    else
                    
                        throw new Exception("file not found " + retval);
                    
                
            
            // MediaStore (and general)
            else if ("content".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
            

                // Return the remote address
                if (isGooglePhotosUri(uri))
                
                    var retval = uri.LastPathSegment;
                    if (File.Exists(retval))
                    
                        return retval;
                    
                    else
                    
                        throw new Exception("file not found " + retval);
                    
                


                var retval2 = getDataColumn(this, uri, null, null);
                if (File.Exists(retval2))
                
                    return retval2;
                
                else
                
                    throw new Exception("file not found " + retval2);
                
            
            // File
            else if ("file".Equals(uri.Scheme, StringComparison.OrdinalIgnoreCase))
            
                var retval = uri.Path;
                if (File.Exists(retval))
                
                    return retval;
                
                else
                
                    throw new Exception("file not found " + retval);
                
            

            throw new Exception("file not found ");
        

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

【问题讨论】:

【参考方案1】:

发现真正的问题是 Google 相册应用没有更新,并且仍在显示已删除的图像。

在手机重启 2 次后,Google 相册应用终于更新了。 所以这看起来更像是 Google Foto 的缓存问题,而不是 xamarin 问题。

【讨论】:

以上是关于Action.Picker 返回无效/错误的 Uri(如何从多个选取的图库 img 中获取路径或字节 [])的主要内容,如果未能解决你的问题,请参考以下文章

Flutter 错误:无效参数:URI 文件中未指定主机:///null

如何从 URL 中获取字符串

ExecuteSqlCommand 返回“无效的对象名称”错误

Facebook 发送按钮返回“无效来源”错误

未捕获的错误:返回的值无效,是不是耗尽了 Gas?

返回列表的总和:错误:参数的“类型”(列表)无效