Xamarin.Forms: How to download an Image, save it locally and display it on screen?

Adam Mrozek picture Adam Mrozek · Jan 7, 2020 · Viewed 7.3k times · Source

I have an issue with opening JPG file in native application on Android. I'm using newest release of Xamarin Essentials, there is some functionality called Launcher. Here is my code

await Launcher.TryOpenAsync("file:///" + localPath);

My local path is file stored in Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);.

Whenever I try to open that file I am getting error:

file:////data/user/0/mypackagename/files/.local/share/Screenshot.jpg exposed beyond app through Intent.getData()

I found several solutions here on stackoverflow, but I don't want to use Intent because my application is designed to be cross-platform (I would like to avoid native platform coding if this possible).

Launcher throws error on iOS also:

canOpenURL: failed for URL: ** -- file:///" - error: "This app is not allowed to query for scheme file

What am I doing wrong here?

Answer

Brandon Minnick picture Brandon Minnick · Jan 7, 2020

Step 1: Download the Image

We can use HttpClient to download the image.

HttpClient.GetByteArrayAsync will retrieve the image data and save it in memory.

In DownloadImage below, we will retrieve the image as a byte[].

static class ImageService
{
    static readonly HttpClient _client = new HttpClient();

    public static Task<byte[]> DownloadImage(string imageUrl)
    {
        if (!imageUrl.Trim().StartsWith("https", StringComparison.OrdinalIgnoreCase))
            throw new Exception("iOS and Android Require Https");

        return _client.GetByteArrayAsync(imageUrl);
    }
}

Step 2 Save Image to Disk

Now that we've downloaded the image, we will save it to disk.

Xamarin.Essentials.Preferences allows us to save items to disk using key-value pairs. Since byte[] is just a pointer to memory, we'll have to first convert the byte[] to base64-string before we can save it to disk.

static class ImageService
{
    static readonly HttpClient _client = new HttpClient();

    public static Task<byte[]> DownloadImage(string imageUrl)
    {
        if (!imageUrl.Trim().StartsWith("https", StringComparison.OrdinalIgnoreCase))
            throw new Exception("iOS and Android Require Https");

        return _client.GetByteArrayAsync(imageUrl);
    }

    public static void SaveToDisk(string imageFileName, byte[] imageAsBase64String)
    {
        Xamarin.Essentials.Preferences.Set(imageFileName, Convert.ToBase64String(imageAsBase64String));
    }
}

Step 3 Retrieve the Image for Display

Now that we've downloaded the image and saved it to disk, we need to be able to retrieve the image from disk to display it on the screen.

GetFromDisk below retrieves the image from disk and converts it to Xamarin.Forms.ImageSource.

static class ImageService
{
    static readonly HttpClient _client = new HttpClient();

    public static Task<byte[]> DownloadImage(string imageUrl)
    {
        if (!imageUrl.Trim().StartsWith("https", StringComparison.OrdinalIgnoreCase))
            throw new Exception("iOS and Android Require Https");

        return _client.GetByteArrayAsync(imageUrl);
    }

    public static void SaveToDisk(string imageFileName, byte[] imageAsBase64String)
    {
        Xamarin.Essentials.Preferences.Set(imageFileName, Convert.ToBase64String(imageAsBase64String));
    }

    public static Xamarin.Forms.ImageSource GetFromDisk(string imageFileName)
    {
        var imageAsBase64String = Xamarin.Essentials.Preferences.Get(imageFileName, string.Empty);

        return ImageSource.FromStream(() => new MemoryStream(Convert.FromBase64String(imageAsBase64String)));
    }
}

Example: Using ImageService in a Xamarin.Forms.ContentPage

class App : Application
{
    public App() => MainPage = new MyPage();
}

class MyPage : ContentPage
{
    readonly Image _downloadedImage = new Image();

    public MyPage()
    {
        Content = _downloadedImage;
    }

    protected override  async void OnAppearing()
    {
        const string xamrainImageUrl = "https://cdn.dribbble.com/users/3701/screenshots/5557667/xamarin-studio-1_2x_4x.png"
        const string xamarinImageName = "XamarinLogo.png";

        var downloadedImage = await ImageService.DownloadImage(xamrainImageUrl);

        ImageService.SaveToDisk(xamarinImageName, downloadedImage);

        _downloadedImage.Source = ImageService.GetFromDisk(xamarinImageName);
    }
}

enter image description here