Xamarin Forms: Issue with downloading image to device

Sreejith Sreenivasan 691 Reputation points
2023-05-01T15:22:30.17+00:00

I follow this blog to implement downloading image from URL and saving it to device.

My code:

IDownloader.cs

public interface IDownloader
{
	void DownloadFile(string url, string folder);
	event EventHandler<DownloadEventArgs> OnFileDownloaded;
}
public class DownloadEventArgs : EventArgs
{
	public bool FileSaved = false;
	public DownloadEventArgs(bool fileSaved)
	{
		FileSaved = fileSaved;
	}
}

AndroidDownloader.cs

[assembly: Dependency(typeof(AndroidDownloader))]
namespace MyApp.Droid.Renderer
{
	public class AndroidDownloader : IDownloader
	{
		public event EventHandler<DownloadEventArgs> OnFileDownloaded;

		public void DownloadFile(string url, string folder)
		{
			string pathToNewFolder = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, folder);
			Directory.CreateDirectory(pathToNewFolder);

			try
			{
				WebClient webClient = new WebClient();
				webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
				string pathToNewFile = Path.Combine(pathToNewFolder, Path.GetFileName(url));
				webClient.DownloadFileAsync(new Uri(url), pathToNewFile);
			}
			catch (Exception ex)
			{
				if (OnFileDownloaded != null)
					OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
			}
		}

		private void Completed(object sender, AsyncCompletedEventArgs e)
		{
			if (e.Error != null)
			{
				if (OnFileDownloaded != null)
					OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
			}
			else
			{
				if (OnFileDownloaded != null)
					OnFileDownloaded.Invoke(this, new DownloadEventArgs(true));
			}
		}
	}
}

On my xaml.cs (I have added permission check on LIVE running)

IDownloader downloader = DependencyService.Get<IDownloader>();

private async void DownloadImage(object sender, EventArgs e)
{
	try
	{
		var status = await CrossPermissions.Current.CheckPermissionStatusAsync<StoragePermission>();
		if (status != PermissionStatus.Granted)
		{
			if (await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Plugin.Permissions.Abstractions.Permission.Storage))
			{
				await DisplayAlert("Storage permission", "Need storage permision to download images.", "OK");
			}
			status = await CrossPermissions.Current.RequestPermissionAsync<StoragePermission>();
		}

		if (status == PermissionStatus.Granted)
		{
			downloader.DownloadFile("http://www.dada-data.net/uploads/image/hausmann_abcd.jpg", "My App");
		}
		else if (status != PermissionStatus.Unknown)
		{
			//location denied
		}
	}
	catch (Exception ex)
	{
		//Something went wrong
	}
}

private async void OnFileDownloaded(object sender, DownloadEventArgs e)
{
	await PopupNavigation.Instance.PopAsync();
	if (e.FileSaved)
	{
		UserDialogs.Instance.Toast("The image saved Successfully.");
	}
	else
	{
		UserDialogs.Instance.Toast("Error while saving the image.");
	}
}

IosDownloader.cs

[assembly: Dependency(typeof(IosDownloader))]

namespace MyApp.iOS.Renderer
{
	public class IosDownloader : IDownloader
	{
		public event EventHandler<DownloadEventArgs> OnFileDownloaded;

		public void DownloadFile(string url, string folder)
		{
			string pathToNewFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), folder);
			Directory.CreateDirectory(pathToNewFolder);

			try
			{
				WebClient webClient = new WebClient();
				webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
				string pathToNewFile = Path.Combine(pathToNewFolder, Path.GetFileName(url));
				webClient.DownloadFileAsync(new Uri(url), pathToNewFile);
			}
			catch (Exception ex)
			{
				if (OnFileDownloaded != null)
					OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
			}
		}

		private void Completed(object sender, AsyncCompletedEventArgs e)
		{
			if (e.Error != null)
			{
				if (OnFileDownloaded != null)
					OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
			}
			else
			{
				if (OnFileDownloaded != null)
					OnFileDownloaded.Invoke(this, new DownloadEventArgs(true));
			}
		}
	}

I am facing below issues:

  1. It is working on android 10 and not working on android 12. Should I add anything else for the proper working on android 12 or above?
  2. Not working on the ios platform. Added the permissions on info.plist.

UPDATE-20230205

I need to view the downloaded images on the device gallery and file manager applications.

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,294 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 35,471 Reputation points Microsoft Vendor
    2023-05-02T03:18:03.41+00:00

    Hello,

    I've modified the interface with the specific implementation method, you could refer to the following complete example:

    IDownloader.cs:

    //Since you could save pictures directly to the album after downloading, it is no longer necessary to set up folders.
    public interface IDownloader 
    {
        void DownloadFile(string url);
        event EventHandler<DownloadEventArgs> OnFileDownloaded;
    }
    public class DownloadEventArgs : EventArgs
    {
        public bool FileSaved = false;
        public DownloadEventArgs(bool fileSaved)
        {
            FileSaved = fileSaved;
        }
    }
    
    
    

    AndroidDownloader:

    [assembly: Dependency(typeof(AndroidDownloader))] 
    namespace App4.Droid
    {
    
        public class AndroidDownloader : IDownloader
        {
            public event EventHandler<DownloadEventArgs> OnFileDownloaded;
            public async void DownloadFile(string url)
            {
    
                try
                {
                    WebClient webClient = new WebClient();
                    webClient.DownloadDataCompleted += Completed;
                    webClient.DownloadDataAsync(new Uri(url));
                }
                catch (System.Exception ex)
                {
                    if (OnFileDownloaded != null)
                        OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
                }
            }
    
            private void Completed(object sender, DownloadDataCompletedEventArgs e)
            {
                if (e.Error != null)
                {
                    if (OnFileDownloaded != null)
                        OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
                }
                else
                {
                    
                    if (OnFileDownloaded != null)
                    {
                        if (Build.VERSION.SdkInt >= BuildVersionCodes.Q)
                        {
                            var contentValues = new ContentValues();
                            contentValues.Put(MediaStore.IMediaColumns.DisplayName, "test.png");
                            contentValues.Put(MediaStore.Files.IFileColumns.MimeType, "image/png");
                            contentValues.Put(MediaStore.IMediaColumns.RelativePath, "Pictures/relativePath");
                            try
                            {
                                var uri = MainActivity.Instance.ContentResolver.Insert(MediaStore.Images.Media.ExternalContentUri, contentValues);
                                var output = MainActivity.Instance.ContentResolver.OpenOutputStream(uri);
                                output.Write(e.Result, 0, e.Result.Length);
                                output.Flush();
                                output.Close();
                            }
                            catch (System.Exception ex)
                            {
                                Console.Write(ex.ToString());
                            }
                            contentValues.Put(MediaStore.IMediaColumns.IsPending, 1);
                        }
                        OnFileDownloaded.Invoke(this, new DownloadEventArgs(true));
                    }
                        
                }
            }
        }
    }
    
    

    Add the following permissions into AndroidManifest.xml:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    Add the following code into MainActivity.cs:

    public static MainActivity Instance { get; private set; } 
    
    // Then, make the assignment in the OnCreate method. global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
    Instance = this; 
    

    IosDownloader:

    
    [assembly: Dependency(typeof(IosDownloader))] 
    namespace App4.iOS
    {
        public class IosDownloader : IDownloader
        {
            public event EventHandler<DownloadEventArgs> OnFileDownloaded;
            private string pathToNewFile;
            public void DownloadFile(string url)
            {
                try
                {
                    WebClient webClient = new WebClient();
                    webClient.DownloadDataCompleted += Completed;
                    webClient.DownloadDataAsync(new Uri(url));
                }
                catch (System.Exception ex)
                {
                    if (OnFileDownloaded != null)
                        OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
                }
            }
    
            private void Completed(object sender, DownloadDataCompletedEventArgs e)
            {
                if (e.Error != null)
                {
                    if (OnFileDownloaded != null)
                        OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
                }
                else
                {
                    
                    if (OnFileDownloaded != null)
                    {
                        var imageData = NSData.FromArray(e.Result);
                        var image = UIImage.LoadFromData(imageData);
    
                        image.SaveToPhotosAlbum((img, error) =>
                        {
    
                        });
                        OnFileDownloaded.Invoke(this, new DownloadEventArgs(true));
                    }
                        
                }
            }
        }
    }
    
    

    Add the following permission into info.plist:

    
    <key>NSPhotoLibraryAddUsageDescription</key> 
    <string>Please allow access to save photo in your photo library</string>
    

    Best Regards,

    Alec Liu.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our [documentation][1] to enable e-mail notifications if you want to receive the related email notification for this thread.