Search This Blog

Thursday 2 May 2019

Xamarin Forms Save Image to Gallery/Album (Android/iOS)



Create the IMediaService Interface and add following code.

IMediaService.cs
using System;
using System.Collections.Generic;
using System.Text;
 
namespace Mechat.Services
{
    public interface IMediaService
    {
        void SaveImageFromByte(byte[] imageByte,string filename);
    }
}

Now Implement this interface in Platform-specific
Xamarin.Android Project
Create the MediaService.cs Class that Implement IMediaService Interface.

MediaService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Mechat.Droid.Services;
using Mechat.Services;
using Plugin.CurrentActivity;
using Xamarin.Forms;
 
[assembly: Xamarin.Forms.Dependency(typeof(MediaService))]
namespace Mechat.Droid.Services
{
  public class MediaService : IMediaService
    {
        Context CurrentContext => CrossCurrentActivity.Current.Activity;
        public void SaveImageFromByte(byte[] imageByte,string fileName)
        {
            try
            {
                Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
                string path = System.IO.Path.Combine(storagePath.ToString(), fileName);
                System.IO.File.WriteAllBytes(path, imageByte);
                var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
                mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
                CurrentContext.SendBroadcast(mediaScanIntent);
            }
            catch (Exception ex)
            {
 
            }
        }
    }
}

Note:   For accessing the CurrentContext  Install the NuGet Package (Plugin.CurrentActivity) from NuGet Package Manager  Also check for the external storage permission.


Xamarin.iOS Project
Create the MediaService.cs Class that Implement IMediaService Interface.

MediaService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Foundation;
using Mechat.iOS.Services;
using Mechat.Services;
using UIKit;
[assembly: Xamarin.Forms.Dependency(typeof(MediaService))]
namespace Mechat.iOS.Services
{
    public class MediaService : IMediaService
    {
        public void SaveImageFromByte(byte[] imageByte,string fileName)
        {
            var imageData = new UIImage(NSData.FromArray(imageByte));
            imageData.SaveToPhotosAlbum((image, error) =>
            {
                //you can retrieve the saved UI Image as well if needed using  
                //var i = image as UIImage;  
                if (error != null)
                {
                    Console.WriteLine(error.ToString());
                }
            });
        }
    }
 
}

How to use in Xamarin.Forms
From the code-behind side, you can call the Interface method SaveImageFromByte to save image in Gallery.

use dependency service to save image.

DependencyService.Get<IMediaService>().SaveImageFromByte(fileName, imageBytes);

2 comments:

  1. Android Error at line System.IO.File.WriteAllBytes(path, bytes);

    {System.UnauthorizedAccessException: Access to the path "/storage/emulated/0/Pictures/XF_Downloads" is denied.
    at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x001aa] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/mcs/class/corlib/System.IO/FileStream.cs:239
    at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/mcs/class/corlib/System.IO/FileStream.cs:91
    at (wrapper remoting-invoke-with-check) System.IO.FileStream..ctor(string,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare)
    at System.IO.File.InternalWriteAllBytes (System.String path, System.Byte[] bytes) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs:429
    at System.IO.File.WriteAllBytes (System.String path, System.Byte[] bytes) [0x00039] in /Users/builder/jenkins/workspace/archive-mono/2019-10/android/release/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs:420
    at MediaSample.Droid.Classes.AndroidDownloader.DownloadFile (System.String url, System.String folder) [0x000d2] in /Users/tislap64/Downloads/MediaPlugin-master/samples/MediaSample/MediaSample.Droid/Classes/AndroidDownloader.cs:47 }

    ReplyDelete
    Replies
    1. public async void SaveQRAsImage(string text) //ImageSource image
      {
      var storageWrite = await Permissions.RequestAsync();
      var storageRead = await Permissions.RequestAsync();

      if(storageWrite == PermissionStatus.Granted && storageRead == PermissionStatus.Granted)
      {
      try
      {
      var barcodeWriter = new ZXing.Mobile.BarcodeWriter
      {
      Format = ZXing.BarcodeFormat.QR_CODE,
      Options = new ZXing.Common.EncodingOptions
      {
      Width = 1000,
      Height = 1000,
      Margin = 10
      }
      };

      barcodeWriter.Renderer = new ZXing.Mobile.BitmapRenderer();
      var bitmap = barcodeWriter.Write(text);
      var stream = new MemoryStream();
      bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream); // this is the diff between iOS and Android
      stream.Position = 0;

      byte[] imageData = stream.ToArray();

      var dir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDcim);
      var pictures = dir.AbsolutePath;
      //adding a time stamp time file name to allow saving more than one image... otherwise it overwrites the previous saved image of the same name
      string name = "MY_QR" + System.DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".jpg";
      string filePath = System.IO.Path.Combine(pictures, name);

      System.IO.File.WriteAllBytes(filePath, imageData);
      //mediascan adds the saved image into the gallery
      var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
      mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(filePath)));

      Xamarin.Forms.Forms.Context.SendBroadcast(mediaScanIntent);

      }
      catch (System.Exception e)
      {
      System.Console.WriteLine(e.ToString());

      }
      }
      }

      This is how I worked it out on Android. The permission error will go away

      Delete

Popular Posts