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