【问题标题】:Xamarin: Saving files to external storage on Android with an API level >=29Xamarin:将文件保存到 Android 上 API 级别 >=29 的外部存储
【发布时间】:2020-09-30 20:43:56
【问题描述】:

我正在尝试将文件导出到 Xamarin 中 Android 手机的 public 外部存储,作为备份数据库。但是,在最新版本的 Android 手机 (11.0 - API30) 中,不能使用 manifest.xml<application> 标记的属性 android:requestLegacyExternalStorage="true" 选择退出范围存储。

在尝试创建文件之前,我确保已授予 READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 权限。尽管如此,在尝试创建文件时,仍会引发 System.UnauthorizedAccessException 异常。

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

    // ....
}

那么问题来了:如何将新文件写入 API 级别为 29 或更高级别的 Android 设备的公共外部存储?


到目前为止我找到的所有资源,也许你可以收集到比我更多的信息:

【问题讨论】:

  • 你找到答案了吗?
  • @ShubhamTyagi,不幸的是我还没有找到解决方案。您可以考虑变通办法(即让用户通过电子邮件在某处发送文件),但没有真正的解决方案:(

标签: xamarin xamarin.android android-10.0 android-external-storage android-11


【解决方案1】:

试试这个,我使用 dependency service 在 Native Android 中调用这个方法,并从它们的字节数组中保存像 docx 和 pdf 这样的文件。

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

【讨论】:

  • 当我尝试这样做时,OutputStream os ... 行抛出异常:Java.IO.FileNotFoundException: /storage/emulated/0/Download/StocksMobile-201011T01:24:22.db3: open failed: EPERM (Operation not permitted) ---&gt; Android.Systems.ErrnoException: open failed: EPERM (Operation not permitted)... 有什么想法吗?我确保写权限被授予
【解决方案2】:

您使用的路径不正确,请尝试以下文件路径。

Context context = Android.App.Application.Context;
var filePath = context.GetExternalFilesDir(Android.OS.Environment.DirectoryDocuments);

请参阅https://forums.xamarin.com/discussion/comment/422501/#Comment_422501

【讨论】:

  • context.GetExternalFilesDir(...) 返回的路径,指的是 private 外部,而我正在寻找一种将文件保存到 public 的方法外部存储,以便用户可以与文件交互...
  • 面向 Android 10 及更高版本的应用程序不再有权访问公共外部存储目录。检查developer.android.com/reference/android/os/…
  • 我明白,但是应用程序可以将文件写入外部存储,以便用户可以与它们进行交互。例如:CamScanner(将文件保存到存储)、Reddit(下载帖子图像/视频)等。如果他们无法访问外部存储,这怎么可能呢?
  • 在 Android 11 上运行但以 Android 10(API 级别 29)为目标的应用仍然可以请求 requestLegacyExternalStorage 属性。将应用更新为面向 Android 11 后,系统会忽略 requestLegacyExternalStorage 标志,因此您必须将目标设置为 Android 10。
  • 在 Android 11 上尝试过,当应用针对 Android 10 时,仍然无法运行...
猜你喜欢
  • 2011-12-14
  • 2020-12-29
  • 2020-08-08
  • 1970-01-01
  • 2021-04-14
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
相关资源
最近更新 更多