Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Xamarin.Forms Saving file in filesystem

I try to download file from web and save to any of Environment.SpecialFolder locations. No matter of what approach I try - I always get

System.UnauthorizedAccessException: 'Access to the path 'any possible path I try (even beyond Environment.SpecialFolder)' is denied.'

I tried accessing filesystem on UWP and Android - both same exception.

What I tried with no luck so far:

  • I tried to do this via PCL and via each platform individually using DependencyService.
  • Checked if folders are read-only
  • Started Visual Studio with administrator privileges
  • Changed Debug to Release
  • Tried Xam.Plugins.DownloadManager

  • Code sample:
    var webClient = new WebClient();
    webClient.DownloadDataCompleted += (s, e) => {
       var bytes = e.Result;
       string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
       string localFilename = "downloaded.jpg";
       string localPath = Path.Combine(documentsPath, localFilename);
       File.WriteAllBytes(localPath, bytes);
    };
    webClient.DownloadDataAsync(new Uri(url));
    
    like image 584
    Bibipkins Avatar asked Jul 03 '18 10:07

    Bibipkins


    3 Answers

    You are facing a permissions issue.

    First, you will have to add in your AndroidManifest:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    And since Android Marshmallow, you need to ask the user for the permissions, so I advise to use the package Permissions.Plugin

    And add in your MainActivity:

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {
        PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
    

    You can check in runtime if you have the permissions by:

    var status = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage);
        if (status != PermissionStatus.Granted)
        {
            if(await CrossPermissions.Current.ShouldShowRequestPermissionRationaleAsync(Permission.Storage))
            {
                await DisplayAlert("Need storage, "Request storage permission", "OK");
            }
    
            var results = await CrossPermissions.Current.RequestPermissionsAsync(Permission.Storage);
            //Best practice to always check that the key exists
            if(results.ContainsKey(Permission.Storage))
                status = results[Permission.Storage];
        }
    

    For further information you can check this blog post explaining all the permissions in Android - https://devblogs.microsoft.com/xamarin/requesting-runtime-permissions-in-android-marshmallow/

    like image 115
    Bruno Caceiro Avatar answered Nov 02 '22 01:11

    Bruno Caceiro


    In addition to Bruno Caceiro's accepted answer, the UWP permissions you want to declare are as follows: DocumentsLibrary for documents, MusicLibrary for the music folder and so on.

    You can get to these graphically via Right click project -> Properties and then clicking Package Manifest and going to the capabilities tab as shown in the screenshot, or alternatively right-clicking package.appxmanifest and selecting view code to edit the xml. See the code excerpt for how to declare capabilities. Some won't have a representation in the dialog.

    <Capabilities>
        <Capability Name="internetClient" />
        <uap:Capability Name="documentsLibrary"/>
        <uap:Capability Name="picturesLibrary" />
    </Capabilities>
    

    Edit additionally, you can also access some restricted folders by using the rescap (restricted capability) namespace to add <rescap:Capability Name="broadFileSystemAccess" /> to gain access to any file/folder the user has access to.

    Capabilities designer

    like image 41
    Fabulous Avatar answered Nov 02 '22 01:11

    Fabulous


    I Had the Same problem on Android, i realized the problem is because

    1. I Had to ask permission on Runtime , keep in mind that you need both ACCESS_COARSE_LOCATION and WRITE_EXTERNAL_STORAGE permissions

    2. I was using wrong Path, my main problem was to get the path pragmatically which i couldn't solve and I solved it by typing the path Manually. i believe if you use the correct full path(etc storage/emulated/0/) you're not going to face any problem.

    like image 40
    hadi Avatar answered Nov 02 '22 00:11

    hadi