简体   繁体   English

如何从 Xamarin Forms 中的图库中 select 多张图片并保存到所需的文件路径?

[英]How to select multiple images from gallery in Xamarin Forms and save to desired file path?

I have the following code to open gallery.我有以下代码来打开图库。

var imageIntent = new Intent(
Intent.ActionPick);
imageIntent.SetType("image/*");
mageIntent.PutExtra(Intent.ExtraAllowMultiple, true);
imageIntent.SetAction(Intent.ActionGetContent);
StartActivityForResult(
Intent.CreateChooser(imageIntent, "Select photo"), 1);

And I stuck at the following code which is the OnActivityResult in my MainActivity.cs我坚持使用以下代码,即 MainActivity.cs 中的 OnActivityResult

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

        if((requestCode==1)&&(resultCode==Result.Ok))
        {
            if(data!=null)
            {
                ClipData clipData = data.ClipData;
                if(clipData!=null)
                {
                    for (int i = 0; i < clipData.ItemCount; i++)
                    {
                        ClipData.Item item = clipData.GetItemAt(i);
                        Android.Net.Uri selectedImage = item.Uri;

                        //How to convert Android.Net.Uri to a image file?


                    }
                }
            }
        }
    }

The Android.Net.Uri is something that I never seen before, the path is just like content/android/document/documents/31857. Android.Net.Uri 是我以前从未见过的,路径就像content/android/document/documents/31857。 Yes, I can select multiple photo from the gallery, but I just don't know how to convert Android.Net.Uri to an legal image file?是的,我可以从画廊 select 多张照片,但我只是不知道如何将 Android.Net.Uri 转换为合法的图像文件? My final goal is to save the selected image to my desired file path like storage/emulated/0... Or is there any better way to select multiple images and save to the desired file?我的最终目标是将所选图像保存到我想要的文件路径,如 storage/emulated/0... 或者有没有更好的方法来 select 多个图像并保存到所需的文件? Thank you.谢谢你。

You could use Dependency Service您可以使用依赖服务

Creating the interface in forms在表单中创建界面

namespace xxx
{
    public interface IPhotoPickerService
    {
        Task<Dictionary<string,Stream>> GetImageStreamAsync();
    }
}

in iOS在 iOS 中

[assembly: Dependency (typeof (PhotoPickerService))]
namespace xxx.iOS
{
    public class PhotoPickerService : IPhotoPickerService
    {
        TaskCompletionSource<Dictionary<string, Stream>> taskCompletionSource;
        UIImagePickerController imagePicker;

        Task<Dictionary<string, Stream>> IPhotoPickerService.GetImageStreamAsync()
        {
            // Create and define UIImagePickerController
            imagePicker = new UIImagePickerController
            {
                SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
                MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
            };

            // Set event handlers
            imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
            imagePicker.Canceled += OnImagePickerCancelled;

            // Present UIImagePickerController;
            UIWindow window = UIApplication.SharedApplication.KeyWindow;
            var viewController = window.RootViewController;
            viewController.PresentModalViewController(imagePicker, true);

            // Return Task object
            taskCompletionSource = new TaskCompletionSource<Dictionary<string, Stream>>();
            return taskCompletionSource.Task;
        }

       

        void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
        {
            UIImage image = args.EditedImage ?? args.OriginalImage;

            if (image != null)
            {
                // Convert UIImage to .NET Stream object
                NSData data;
                if (args.ReferenceUrl.PathExtension.Equals("PNG") || args.ReferenceUrl.PathExtension.Equals("png"))
                {
                    data = image.AsPNG();
                }
                else
                {
                    data = image.AsJPEG(1);
                }
                Stream stream = data.AsStream();

                UnregisterEventHandlers();

                Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
                dic.Add(args.ImageUrl.ToString(), stream);

                // Set the Stream as the completion of the Task
                taskCompletionSource.SetResult(dic);
            }
            else
            {
                UnregisterEventHandlers();
                taskCompletionSource.SetResult(null);
            }
            imagePicker.DismissModalViewController(true);
        }

        void OnImagePickerCancelled(object sender, EventArgs args)
        {
            UnregisterEventHandlers();
            taskCompletionSource.SetResult(null);
            imagePicker.DismissModalViewController(true);
        }

        void UnregisterEventHandlers()
        {
            imagePicker.FinishedPickingMedia -= OnImagePickerFinishedPickingMedia;
            imagePicker.Canceled -= OnImagePickerCancelled;
        }

        
    }
}

in Android在安卓中

in MainActivity在主活动中

public class MainActivity : FormsAppCompatActivity
{
    ...
    // Field, property, and method for Picture Picker
    public static readonly int PickImageId = 1000;

    public TaskCompletionSource<Dictionary<string,Stream>> PickImageTaskCompletionSource { set; get; }

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

        if (requestCode == PickImageId)
        {
            if ((resultCode == Result.Ok) && (intent != null))
            {
                Android.Net.Uri uri = intent.Data;
                Stream stream = ContentResolver.OpenInputStream(uri);

                Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
                dic.Add(uri.ToString(), stream);
                // Set the Stream as the completion of the Task
                PickImageTaskCompletionSource.SetResult(dic);
            }
            else
            {
                PickImageTaskCompletionSource.SetResult(null);
            }
        }
    }
}
[assembly: Dependency(typeof(PhotoPickerService))]
namespace xxx.Droid
{
    public class PhotoPickerService : IPhotoPickerService
    {
        public Task<Dictionary<string,Stream>> GetImageStreamAsync()
        {
            // Define the Intent for getting images
            Intent intent = new Intent();
            intent.SetType("image/*");
            intent.SetAction(Intent.ActionGetContent);

            // Start the picture-picker activity (resumes in MainActivity.cs)
            MainActivity.Instance.StartActivityForResult(
                Intent.CreateChooser(intent, "Select Picture"),
                MainActivity.PickImageId);

            // Save the TaskCompletionSource object as a MainActivity property
            MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Dictionary<string,Stream>>();

            // Return Task object
            return MainActivity.Instance.PickImageTaskCompletionSource.Task;
        }
    }
}

invoke it调用它

Dictionary<string, Stream> dic = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();

Stream stream;
string path;

foreach ( KeyValuePair<string, Stream> currentImage in dic )
{
   stream = currentImage.Value;

   // save it    

}

thanks for you help.谢谢你的帮助。 Below is my final solution for those who need help.以下是我为需要帮助的人提供的最终解决方案。

private string GetPathToImage(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;
    }

After that you can do whatever you want, cheers!之后就可以为所欲为了,加油! Taken from: Get Path of Gallery Image Xamarin?取自: 获取图库图像 Xamarin 的路径?

Here is the iOS implementation for picking multiple photos from Gallery in Xamarin. You will get a list of byte arrays as the result of calling Task<List<byte[]>> PickPhotos().下面是从 Xamarin 中的图库中选取多张照片的 iOS 实现。作为调用 Task<List<byte[]>> PickPhotos() 的结果,您将获得字节 arrays 的列表。 Android implementation works well in crossmedia plugin by Montemagno but iOS is buggy. Android 实现在 Montemagno 的跨媒体插件中运行良好,但 iOS 存在问题。 This should fix it.这应该解决它。 I am using the new PHPickerViewController to get this done.我正在使用新的 PHPickerViewController 来完成这项工作。 Also included a native image resizer routine to size down the photos if needed.还包括一个本机图像大小调整器例程,以在需要时缩小照片的尺寸。

    public class PhotoService 
{
    public override async Task<List<byte[]>> PickPhotos()
    {
        var imagePicker = new PHPickerViewController(new PHPickerConfiguration
        {
            Filter = PHPickerFilter.ImagesFilter,
            SelectionLimit = 50,
        });
        var window = UIApplication.SharedApplication.KeyWindow;
        var vc = window.RootViewController;
        while (vc.PresentedViewController != null)
        {
            vc = vc.PresentedViewController;
        }
        var tcs = new TaskCompletionSource<Task<List<byte[]>>>();

        imagePicker.Delegate = new PPD
        {
            CompletedHandler = res => tcs.TrySetResult(PickerResultsToMediaFile(res))
        };
        await vc.PresentViewControllerAsync(imagePicker, true);
        var resultTask = await tcs.Task;
        var result = await resultTask;
        await vc.DismissViewControllerAsync(true);
        imagePicker?.Dispose();
        return result;
    }

    private async Task<List<byte[]>> PickerResultsToMediaFile(PHPickerResult[] res)
    {
        var results = new List<byte[]>();
        var tcs = new TaskCompletionSource<NSObject>();
        foreach (var item in res)
        {
            try
            {
                var provider = item.ItemProvider;
                var suggestedName = provider.SuggestedName;
                var identifiers = provider?.RegisteredTypeIdentifiers;

                var identifier = (identifiers?.Any(i => i.StartsWith(UTType.LivePhoto)) ?? false)
                    && (identifiers?.Contains(UTType.JPEG) ?? false)
                    ? identifiers?.FirstOrDefault(i => i == UTType.JPEG)
                    : identifiers?.FirstOrDefault();
                if (string.IsNullOrWhiteSpace(identifier))
                    continue;
                var fileName = $"{provider?.SuggestedName}.{GetTag(identifier, UTType.TagClassFilenameExtension)}";
                var stream = (await provider.LoadDataRepresentationAsync(identifier))?.AsStream();
                byte[] bytes;
                using (MemoryStream ms = new MemoryStream())
                {
                    stream.CopyTo(ms);
                    bytes = ms.ToArray();
                }
                results.Add(bytes);
            }
            catch(Exception)
            {
                continue;
            }
        }
        return results;
    }
public override byte[] ResizeImage(byte[] imageData)
    {
        float width, height;
        UIImage originalImage = ImageFromByteArray(imageData);
        if (originalImage == null)
        {
            return imageData;
        }
        UIImageOrientation orientation = originalImage.Orientation;

        if (originalImage.Size.Width > originalImage.Size.Height)
        {
            if (originalImage.Size.Width > 1920)
            {
                width = 1920;
                height = 1920 * (float)originalImage.Size.Height / (float)originalImage.Size.Width;
            }
            else
            {
                width = (float)originalImage.Size.Width;
                height = (float)originalImage.Size.Height;
            }
        }
        else
        {
            if (originalImage.Size.Height > 1920)
            {
                height = 1920;
                width = 1920 * (float)originalImage.Size.Width / (float)originalImage.Size.Height;
            }
            else
            {
                width = (float)originalImage.Size.Width;
                height = (float)originalImage.Size.Height;
            }
        }

        //create a 24bit RGB image
        using (CGBitmapContext context = new CGBitmapContext(IntPtr.Zero,
                                             (int)width, (int)height, 8,
                                             4 * (int)width, CGColorSpace.CreateDeviceRGB(),
                                             CGImageAlphaInfo.PremultipliedFirst))
        {

            RectangleF imageRect = new RectangleF(0, 0, width, height);

            // draw the image
            context.DrawImage(imageRect, originalImage.CGImage);

            UIImage resizedImage = UIImage.FromImage(context.ToImage(), 0, orientation);

            // save the image as a jpeg
            return resizedImage.AsJPEG().ToArray();
        }
    }
    public UIImage ImageFromByteArray(byte[] data)
    {
        if (data == null)
        {
            return null;
        }

        UIImage image;
        try
        {
            image = new UIImage(Foundation.NSData.FromArray(data));
        }
        catch (Exception e)
        {
            Console.WriteLine("Image load failed: " + e.Message);
            return null;
        }
        return image;
    }
    protected internal static string GetTag(string identifier, string tagClass)
        => UTType.CopyAllTags(identifier, tagClass)?.FirstOrDefault();
}
class PPD : PHPickerViewControllerDelegate
{
    public Action<PHPickerResult[]> CompletedHandler { get; set; }

    public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results) =>
        CompletedHandler?.Invoke(results?.Length > 0 ? results : null);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM