8

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));
    
    Bibipkins
    • 442
    • 1
    • 7
    • 19
    • Does your app have the necessary permissions on each platform? – Fabulous Jul 03 '18 at 10:52
    • What?? How can I possibly do that in Visual Studio 2017? I can set required permissions for Android but it did not help. I have no idea how to can I get those permissions – Bibipkins Jul 03 '18 at 10:56
    • Have a look at the answer from @BrunoCaceiro which addresses the same thing and see if it solves your answer. – Fabulous Jul 03 '18 at 11:52

    3 Answers3

    4

    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/

    Brett Rigby
    • 6,101
    • 10
    • 46
    • 76
    Bruno Caceiro
    • 7,035
    • 1
    • 26
    • 45
    2

    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

    Fabulous
    • 2,393
    • 2
    • 20
    • 27
    • Thanks! I spent quite some time making it work before seeing your answer. But I only managed to save files into "KnownFolders.SavedPictures". Now I can save in any of "KnownFolders" – Bibipkins Jul 03 '18 at 14:20
    1

    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.

    hadi
    • 11
    • 2