Xamarin:Android:System.UnauthorizedAccessException:访问路径被拒绝

22

所以我正在尝试创建一个文件,但是遇到了 System.UnauthorizedAccessException: 访问被拒绝的路径"/DownloadJitters"。我不确定它是否是权限问题(我已经尝试写入外部存储器,但没有起作用),或者其他什么问题。此外,我正在尝试找出一个好的地方来写这些文件,因为我希望它们不容易被发现。有什么想法吗?下面是代码:

public void favouriteList(MainActivity av, Ordering o, string favouriteName, string totalCost, JittersListView jlv)
    {
        //Checks Directory exists
        if (File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt") == false)
        {
            Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
            File.Create(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt");
        }

        if (File.Exists(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt") == false)
        {
            var fav = File.Create(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt");
            fav.Close();
            string file = Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt";
            string added = null;
            int current = 0;
            while (true)
            {
                if (current < jlv.Count)
                {
                    JittersListItem jli = jlv[current];
                    added += jli.Top + "|" + jli.Bottom + "|" + jli.itemPic + "|" + jli.itemDes + System.Environment.NewLine;
                    current++;
                }
                else
                {
                    break;
                }
            }
            File.AppendAllText(file, favouriteName + "|" + totalCost + added);
        }
        else
        {
            new AlertDialog.Builder(av)
                    .SetMessage("Please use a different name, this one has been taken.")
                    .Show();
        }
    }
5个回答

25

首先将以下权限添加到您的清单文件中:

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

自 Android 6.0(API 23)开始,你还需要手动请求权限,在你的 Xamarin.Android 项目的 MainActivity.cs 中添加以下代码:

if ((ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted)
            || (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted))
        {
            ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, REQUEST);
        }
自 Android 10 开始,您可能还需要在清单文件中添加 android:requestLegacyExternalStorage 属性,如下所示:
<application android:requestLegacyExternalStorage="true" />

更新

在完成所有操作后,如果您尝试将文件保存到SDCARD的任何路径上,可能仍会在Android 11或更高版本上出现异常。您只能默认使用应用程序的EXTERNAL STORAGE和INTERNAL STORAGE路径。

应用程序的私有EXTERNAL STORAGE路径:

Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath

您的应用程序的私有内部存储路径:

System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)

如果您需要访问Android 11或更高版本上SDCARD上的任何路径,您应该请求管理所有文件访问权限。

private void RequestStorageAccess()
{
    if (!Environment.IsExternalStorageManager) 
    {
        StartActivityForResult(new Intent(Android.Provider.Settings.ActionManageAllFilesAccessPermission), 3);
    }
}

这将弹出一个活动列表,其中列出了所有具有对所有文件访问权限的应用程序,用户应该点击您的应用并启用权限。

但要使您的应用程序出现在其中,您应该将此权限添加到清单中:

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

同时,您还需要确保使用 Android 11 或更高版本编译您的应用程序。

enter image description here

这里是权限活动界面:

enter image description here

enter image description here

由于某些原因,我能够在没有此权限的情况下将文件保存到文档文件夹中,但在删除文件后,我无法再创建同名的文件,也无法列出所有文件,这就是为什么即使使用文档目录也需要弹出此窗口的原因。

只有在真正需要的情况下才使用“管理所有文件”权限

如果您想在 Google Play 上发布带有“管理所有文件”权限的应用程序,则必须在 Google Play 上填写“权限声明表单”,因为它被认为是一种“高风险或敏感权限”,这里有更多信息,并且需要经过 Google 的批准:

https://support.google.com/googleplay/android-developer/answer/9214102?hl=en#zippy=

何时要请求此权限:

https://support.google.com/googleplay/android-developer/answer/10467955?hl=en


这实际上导致了我遇到了一个不幸的调试和快速部署问题。这个问题出现在编辑了包含<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />及相关行的清单文件时。 - Harish Patil

13

我通过将保存位置更改为System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)来解决了问题。

不要问我为什么会这样,因为它们需要相同的权限,但它确实起作用了。


3

Xamarin.Forms(Android解决方案)

MainActivity.cs

对于针对 Android 5.1(API 级别 22) 或更低版本的应用程序,无需进行进一步操作。 将在 Android 6.0(API 级别 23) 或更高版本上运行的应用程序应请求运行时权限检查。
protected override void OnCreate(Bundle bundle)
{
    if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
    {
        if (!(CheckPermissionGranted(Manifest.Permission.ReadExternalStorage) && !CheckPermissionGranted(Manifest.Permission.WriteExternalStorage)))
        {
            RequestPermission();
        }
    }
    LoadApplication(new App());
}

private void RequestPermission()
{
    ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, 0);
}

public bool CheckPermissionGranted(string Permissions)
{
    // Check if the permission is already available.
    if (ActivityCompat.CheckSelfPermission(this, Permissions) != Permission.Granted)
    {
        return false;
    }
    else
    {
        return true;
    }
}

这应该被标记为完美答案,因为其他答案引导我遇到了与快速部署相关的更多错误。感谢Divyesh_08,它对我有用。 - Harish Patil

0

0

这看起来像是复制粘贴错误 - 你应该学会将常见的代码和表达式重构为一个值并重复使用它。

//Checks Directory exists
if (File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt") == false)
{
    Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
    File.Create(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt");
}

假设Android.OS.Environment.DirectoryDownloads的值为/Downloads。现在逐行查看代码(最好使用调试器):

File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt")

这里的参数值将是"/Downloads/Jitters/FavouritesListAdded.txt" - OK

Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");

这里的字面字符串没有前导斜杠,因此值将是:/DownloadsJitters/FavouriteList - 我猜你可能想要它成为 /Downloads/Jitters/FavouriteList

与其确保代码中的所有6个路径表达式都添加了斜杠 - 不如创建一个变量,其中包含路径值,并重复使用它。


好的,我已经加入了斜杠,但错误仍然存在 :/ 不过现在不是 "/DownloadJitters" 而是 "/Download"。 - Euan Hollidge
更正了上面的注释,但路径为"/Download"的相同错误。 - Euan Hollidge
当我使用标题大小写的文件名保存时,出现了这个错误。当我改成全小写时,它就可以工作了。可能是某个 bug。 - Steven Mark Ford

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接