Xamarin: Saving files to external storage on Android with an API level >=29

4.1k views Asked by At

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:

2

There are 2 answers

6
ColeX On

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 .

1
Shubham Tyagi On

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