Reputation: 80
I've been trying to save a file, more precisely an image, in a shared folder ("Downloads", "Documents", etc...), but it seems that I'm missing something.
This is the MainPage.cs
:
private async void GenerateQRCode()
{
// Generate QR Code
QRCodeGenerator qrCodeGenerator = new QRCodeGenerator();
QRCodeData qrCodeData = qrCodeGenerator.CreateQrCode(ShopName.ToUpper() + ShopLocation.ToUpper(), QRCodeGenerator.ECCLevel.Q);
PngByteQRCode qRCode = new PngByteQRCode(qrCodeData);
byte[] qrCodeBytes = qRCode.GetGraphic(20);
// Set the new image source to be the QR Code
QRCodeImage.Source = ImageSource.FromStream(() => new MemoryStream(qrCodeBytes));
// Get the permissions
PermissionStatus statusWrite = await Permissions.RequestAsync<Permissions.StorageWrite>();
PermissionStatus statusRead = await Permissions.RequestAsync<Permissions.StorageRead>();
// Debug the permissions
await DisplayAlert("Write", statusWrite.ToString(), "OK");
await DisplayAlert("Read", statusRead.ToString(), "OK");
// Get the Downloads path (only Android)
string path = (string)Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);
// Get the final file name
string fileName = Path.Combine(path, "QRCode.png");
// Debug the file name
await DisplayAlert("Debug", fileName, "OK");
// Save the QR Code as a byte[]
File.WriteAllBytes(path, qrCodeBytes);
}
These are the permissions in the AndroidManifest.xml
:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
When I run this code I get this error:
System.UnauthorizedAccessException: 'Access to the path '/storage/emulated/0/Download' is denied.'
I've read that from Android's API 23
the user has to accept the permissions at run-time, but I've tried many solutions, but it still tells me that the access is denied. (The debug messages I get from the code I've posted are (Granted
, Granted
and /storage/emulated/0/Download/QRCode.png
).
I've also read that Android.OS.Environment.GetExternalStoragePublicDirectory
is deprecated, so if anyone has a "newer" way to get the desired directory, feel free to suggest!
UPDATE - MY SOLUTION:
Create a service interface in your "Global Project" (not only in the Android or in the iOS project) to make it more tidy:
public interface IFileService
{
void Save(byte[] data, string name);
}
Create a new DependencyService
in your Android project:
[assembly: Xamarin.Forms.Dependency(typeof(ProjectNamespace.Droid.FileService))]
namespace ProjectNamespace.Droid
{
public class FileService : IFileService
{
public void Save(byte[] data, string name)
{
if (MainActivity.Instance.CheckSelfPermission(Manifest.Permission.WriteExternalStorage) == Android.Content.PM.Permission.Granted)
{
string path = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath;
string filePath = System.IO.Path.Combine(path, name);
FileOutputStream fileOutputStream = new FileOutputStream(new Java.IO.File(filePath));
fileOutputStream.Write(data);
fileOutputStream.Close();
}
else
{
MainActivity.Instance.RequestPermissions(new string[] { Manifest.Permission.WriteExternalStorage }, 0);
}
}
}
}
Finally, in order to use MainActivity
, we need a reference to it, and so I've created a Singleton (yeah, I know, I should have done it in a safer way, but since it was a learning project, I decided to make it in this way):
public static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
...
Instance = this;
...
}
Upvotes: 2
Views: 7408
Reputation: 14956
If you request the WriteExternalStorage
run-time permission.It will work.
You could try it like below:
if (CheckSelfPermission(Manifest.Permission.WriteExternalStorage) == Android.Content.PM.Permission.Granted)
{
string path = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath;
string filePath = System.IO.Path.Combine(path,"QRCode.png");
FileOutputStream fileOutputStream = new FileOutputStream(new Java.IO.File(filePath));
fileOutputStream.Write(qrCodeBytes);
fileOutputStream.Close();
}
else
{
RequestPermissions(new string[] { Manifest.Permission.WriteExternalStorage }, 0);
}
Upvotes: 3
Reputation: 37
Add EXTERNAL permissions.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Then save the files.
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
string localFilename = "downloaded.jpg";
string localPath = Path.Combine(documentsPath, localFilename);
File.WriteAllBytes(localPath, bytes);
You can refer to Xamarin.Forms Saving file in filesystem
Upvotes: -1