【问题标题】:UnauthorizedAccessException when calling FileInfo.Length调用 FileInfo.Length 时出现 UnauthorizedAccessException
【发布时间】:2020-09-23 18:02:59
【问题描述】:

我确实通过 Crashlytics 收到报告,当我致电 FileInfo.Length; 时,我的 Unity 应用程序的一些用户(大约 0.5%)会收到 UnauthorizedAccessException

堆栈跟踪的有趣部分是:

Non-fatal Exception: java.lang.Exception
UnauthorizedAccessException : Access to the path '/storage/emulated/0/Android/data/com.myCompany.myGreatGame/files/assets/myAsset.asset' is denied.
System.IO.__Error.WinIOError (System.IO.__Error)
System.IO.FileInfo.get_Length (System.IO.FileInfo)

对应的文件(每个报告的文件不同)是由同一个应用程序写入(或当前正在写入)的(可能是之前的许多会话)。该调用发生在后台线程中,并且可能同时进行一些写入。但是根据.net doc,这个属性应该被预先缓存(见https://docs.microsoft.com/en-us/dotnet/api/system.io.fileinfo.length?view=netframework-2.0

导致它的整个代码是:

private static long DirSize(DirectoryInfo d) 
{
    long size = 0;
    FileInfo[] fileInfos = d.GetFiles();
    foreach (FileInfo fileInfo in fileInfos) 
    {      
        size += fileInfo.Length;
    }
    ...

有没有人遇到过类似的事情并知道可能是什么原因造成的?

【问题讨论】:

  • 问题肯定出在 C# 中吗?我问是因为错误提到了 java(也有一个阿联酋)
  • 我相信它是 C# - 它总是在堆栈跟踪中显示 java.lang.Exception - 也适用于我所有可能与 Firebase/Crashlytics/Unity 相关的其他异常。之前的方法也来自我的 C# 代码。
  • 请添加完整的代码或至少告诉具有d 的值或更确切地说是您要访问的确切文件?好吧,是的,您同时尝试编写也可能是一个问题

标签: c# android .net unity3d


【解决方案1】:

这看起来像是一个非常奇特的错误,因此,我没有证据支持我的建议。

建议 1:
用户安装了防病毒软件 - 这些应用程序有时像恶意软件一样工作,锁定主机程序未使用的文件来测试它们(特别是如果它们想要防止恶意行为)。这可以解释错误的罕见性质。在 Length 方法调用失败后,我会尝试查看文件的 permissions,这可能会给您(可能还有我们)更多的见解。

建议 2:
在某些情况下,当应用程序主动写入此文件时,您无法读取长度。这永远不会发生,但即使在操作系统中也会发生错误。可能的路径:某些应用程序正在写入文件。文件被修改并写入元数据(包括长度),而您正在从另一个线程读取长度,操作系统锁定文件以防止读取元数据(包括长度),同时写入元数据(可能出于安全原因)

建议 3(也是最可能的):
坏 SD 卡/内存/CPU - 一些随机错误总是会发生,因为您不控制客户端的硬件。我会检查这 0.5% 的错误是否来自一个用户,或者看似来自多个用户,而是由于硬件的其他问题,他们的唯一 ID 重置(检查其他数据,如手机型号,因为这也可能为您提供线索)。

【讨论】:

    【解决方案2】:

    您很可能试图访问您无权访问的文件。有些文件甚至管理员也无法访问。

    您可以使用 Try/Catch 块来处理异常。

    this question

    【讨论】:

    • 但是这些文件是由同一个应用程序先前在 Application.persistentDataPath 的子文件夹中写入的,即 androids ExternalFilesDir(请参阅docs.unity3d.com/ScriptReference/…
    • 权限怎么会同时消失?
    【解决方案3】:

    如果您仔细阅读Microsoft's documentation,它会明确指出:

    1. 如果刷新失败,则会引发 I/O 错误
    2. FileInfo.Length 属性仅在非常精确的案例列表中进行预缓存(GetDirectories、GetFiles、GetFileSystemInfos、EnumerateDirectories、EnumerateFiles、EnumerateFileSystemInfos)。应该通过调用 Refresh() 方法来刷新缓存的信息。

    插值 #1 和 #2 您可以轻松识别问题:当您尝试获取该信息时,您打开了一个带有排他锁的文件,这会导致您出现 #1 中的错误。我建议实现两种不同的逻辑,一个是明显的 try/catch 块,但是因为该块 (a) 性能成本和 (b) 不能解决知道文件大小的逻辑问题,所以您还应该缓存获取排他锁时自己获取这些数据。

    将它们放在内存中的静态表中,一个简单的键/值(文件/大小),并在调用 FileInfo.Length() 之前对其进行检查。基本上,当您获得锁时,您将文件/大小值添加到字典中,完成后将其删除。这样一来,您将永远不会再遇到错误,同时能够计算出相同的目录大小。

    ~皮诺

    【讨论】:

      猜你喜欢
      • 2011-11-21
      • 2014-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-20
      • 1970-01-01
      • 1970-01-01
      • 2015-03-26
      相关资源
      最近更新 更多