1

I'm trying to export files to the public external storage of an Android phone in Xamarin, for a backup DB. However, in the last version of Android phones (11.0 - API30), one can't opt-out of scoped storage using the property android:requestLegacyExternalStorage="true" of the <application> tag in the manifest.xml.

I made sure that the permissions READ_EXTERNAL_STORAGE & WRITE_EXTERNAL_STORAGE are granted before trying to create the file. Still, when trying to create a file, a System.UnauthorizedAccessException exception is thrown.

/* file 1: */
// ....
private async void Export_Tapped (object sender, EventArgs e) {
    // check if permission for writing in external storage is given
    bool canWrite = await FileSystem.ExternalStoragePermission_IsGranted ();

    if (!canWrite) {
        // request permission
        canWrite = await FileSystem.ExternalStoragePermission_Request ();

        if (!canWrite) {
            // alert user

            return;
        }
    }

    // find the directory to export the db to, based on the platform
    string fileName = "backup-" + DateTime.Now.ToString ("yyMMddThh:mm:ss") + ".db3";
    string directory = FileSystem.GetDirectoryForDatabaseExport ();     // returns "/storage/emulated/0/Download"
    string fullPath = Path.Combine (directory, fileName);

    // copy db to the directory
    bool copied = false;
    if (directory != null)
        copied = DBConnection.CopyDatabase (fullPath);

    if (copied)
        // alert user
    else
        // alert user
}
// ....

/* file 2: */
class DBConnection 
{
    private readonly string dbPath;
    
    // ....

    public bool CopyDatabase(string path) 
    {
        byte[] dbFile = File.ReadAllBytes(dbPath);
        File.WriteAllBytes(path, dbFile);        // --> this is where the exception is thrown <--
        
        return true;
    }

    // ....
}

So the question stands: how does one write a new file to the public external storage of an Android device with an API level of 29 or more?


All the resources I have found so far, maybe you can gather more information than I did:

E. Epstein
  • 739
  • 2
  • 11
  • 26

2 Answers2

0

Try This , I use a dependency service to call this method in Native Android and save files like docx and pdf from their by byte array.

public async Task<bool> CreateFile(string fileName, byte[] docBytes, string fileType)
        {
            try
            {
                Java.IO.File file = new Java.IO.File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath, fileName + fileType);
                OutputStream os = new FileOutputStream(file);
                os.Write(docBytes);
                os.Close();
            }
            catch
            {
                return false;
            }
            return true;
        }
Shubham Tyagi
  • 798
  • 6
  • 21
  • 2
    When I try to do this, the line `OutputStream os ...` throws an exception: `Java.IO.FileNotFoundException: /storage/emulated/0/Download/StocksMobile-201011T01:24:22.db3: open failed: EPERM (Operation not permitted) ---> Android.Systems.ErrnoException: open failed: EPERM (Operation not permitted)`... Any ideas? I made sure that write permissions **were** given. – E. Epstein Oct 11 '20 at 10:26
-1

The path you use is incorrect, please try the following file path .

Context context = Android.App.Application.Context;
var filePath = context.GetExternalFilesDir(Android.OS.Environment.DirectoryDocuments);

Refer to https://forums.xamarin.com/discussion/comment/422501/#Comment_422501 .

ColeX
  • 14,062
  • 5
  • 43
  • 240
  • The path `context.GetExternalFilesDir(...)` returns, refers to the **private** external, while I'm looking for a way to save a file to the **public** external storage, so that the user can interact with the file... – E. Epstein Oct 01 '20 at 07:59
  • Applications targetting Android 10 and newer no longer have access to public external storage directories. Check https://developer.android.com/reference/android/os/Environment#getExternalStoragePublicDirectory%28java.lang.String%29. – ColeX Oct 01 '20 at 08:10
  • 2
    I understand, however applications can write files to the external storage, such that the user can interact with them. For example: CamScanner (save files to storage), Reddit (download post image/video) etc. So how is this possible if they don't have access to external storage? – E. Epstein Oct 01 '20 at 08:36
  • Apps that run on Android 11 but target Android 10 (API level 29) can still request the requestLegacyExternalStorage attribute. After you update your app to target Android 11, the system ignores the requestLegacyExternalStorage flag, so you have to set the target as Android 10 . – ColeX Oct 06 '20 at 06:15
  • Tried it with on Android 11, when the app targets Android 10, still didn't work... – E. Epstein Oct 06 '20 at 21:44
  • Check https://stackoverflow.com/a/57116787/8187800 . – ColeX Oct 12 '20 at 02:23