【问题标题】:File operations in android Q betaandroid Q beta中的文件操作
【发布时间】:2019-12-13 16:33:40
【问题描述】:

1)目标设置为Android Qandroid.permission.WRITE_EXTERNAL_STORAGE

2) 使用getExternalStorageDirectorygetExternalStoragePublicDirectoryFileOutputStream(file)保存文件抛出

java.io.FileNotFoundException: /storage/emulated/0/myfolder/mytext.txt open failed: ENOENT (No such file or directory)

3) 使用getExternalFilesDirapi 并保存成功,但即使在MediaScannerConnection.scanFile 之后也不会显示。

     /storage/emulated/0/Android/data/my.com.ui/files/Download/myfolder/mytext.txt

在android Q中将文件从内部存储器复制到SDCARD并刷新的最佳方法是什么。

【问题讨论】:

    标签: android fileoutputstream android-10.0


    【解决方案1】:

    在 Android Manifest 的 <application> 标签中添加 android:requestLegacyExternalStorage="true",这将允许您暂时使用所有文件 API。

    【讨论】:

    • 是的,它可以工作。想要在没有那个选项的情况下让它工作。
    • @NitZRobotKoder 除了使用 SAF 之外别无他法。
    • “别无他法”:您也可以从 Android R+ 开始放弃支持并完全忽略该平台。总有一些选择...... :)(我没有投反对票,但很高兴提到这个标志只是Android Q的临时标志,但另一个答案无论如何都更完整......虽然它可能有更明确地提到了 SAF(我只是在这里吃爆米花))
    • android:requestLegacyExternalStorage="true"
    【解决方案2】:

    在 Android Q 中,对于私有文件夹之外的应用,默认情况下禁用直接文件访问。您可以使用以下几种策略:

    1. 使用 manifest 选项 requestLegacyStorage 来获得旧行为,但它不再适用于 Android R,因此它确实是一个短期解决方案;
    2. 使用getExternalFilesDir() 方法保存文件。这是您的私人文件夹,其他应用只有拥有READ_EXTERNAL_STORAGE 权限才能访问这些文件。在这种情况下,最好使用 FileProvider 授予对您文件的其他应用程序的访问权限。
    3. 使用MediaStore API 直接插入您的媒体。这种方法适用于视频、音乐和照片。
    4. 使用StorageManager 类的方法getPrimaryStorageVolume().createOpenDocumentTreeIntent() 请求访问外部主卷。在这种情况下,您需要用户同意并且无论如何您将无法直接使用File api,但是使用DocumentFile 类您有一个非常相似的界面,所以这是更接近旧行为的解决方案。如果您需要在前台和后台执行操作,即无需用户交互,但请求权限的第一次交互除外。
    5. 使用ACTION_CREATE_DOCUMENT 创建一个使用SAF 的文件。如果您在前台执行此操作,此选项有效,因为您需要用户选择文件夹。

    我为第 4 点链接了 Flipper 库,它有助于像在旧的 android 版本中一样管理文件。

    【讨论】:

    • 是否可以选择创建我的名字的文件夹并通过 Document-provider 复制到 SDCARD 中?
    • 不是直接的,但您可以使用选项 4 使用方法 getStorageVolumes 返回的可移动非主存储来询问 SdCard 访问
    • @greywolf82 能否请您进一步解释这一点:3 - 使用 MediaStore API 直接插入您的媒体。这种方法适用于视频、音乐和照片。
    • @greywolf82 我认为我在 android 10 beta 版本中遇到了与 File API 相同的问题。我已经用存储访问框架修复了它。但是在 android Q 官方发布版本中,FileOutputStream(file) 不再抛出这个异常。我没有使用requestLegacyStorage = true,并且我的 tagetsdk 和 compileSdk 是 29。谷歌在最终版本中是否更改了任何内容?
    【解决方案3】:

    1)使用文档提供者https://developer.android.com/guide/topics/providers/document-provider#create

    2)将要求用户保存(文件总数)并存储在文档文件夹中。 https://gist.github.com/neonankiti/05922cf0a44108a2e2732671ed9ef386

    【讨论】:

      【解决方案4】:

      在 Android API 29 及更高版本中,您可以使用以下代码将文件、图像和视频存储到外部存储。

      //首先,如果您使用“android.media.action.IMAGE_CAPTURE”选择文件 然后将该文件存储在应用程序私有 Path(getExternalFilesDir()) 中,如下所示。

      File destination = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName);
      

      //然后使用content provider来访问Media-store,如下所示。

       ContentValues values = new ContentValues(); 
       values.put(MediaStore.Images.Media.TITLE, fileName); 
       values.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);
      

      //如果您想要特定的 mime 类型,请在此处指定您的 mime 类型。否则留空,它将采用默认文件mime类型

      values.put(MediaStore.Images.Media.MIME_TYPE, "MimeType"); 
      values.put(MediaStore.Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000); 
      values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis()); 
      values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/" + "path"); // specify 
       storage path
      

      // 使用内容提供者插入媒体存储,它将返回 URI。

      Uri uri = cxt.getContentResolver().insert(MediaStore.Files.getContentUri("external"), values); 
      

      //使用该URI打开文件。

      ParcelFileDescriptor descriptor = context.getContentResolver().openFileDescriptor(uri,"w"); //"w" specify's write mode
      FileDescriptor fileDescriptor = descriptor.getFileDescriptor();
      

      // 从私有路径读取文件。

      InputStream dataInputStream = cxt.openFileInput(privatePath_file_path);
      

      //将文件写入输出文件流。

       OutputStream output = new FileOutputStream(fileDescriptor);
       byte[] buf = new byte[1024];
       int bytesRead;
       while ((bytesRead = dataInputStream.read(buf)) > 0) 
       {
        output.write(buf, 0, bytesRead);
       }
       datanputStream.close();
       output.close();
      }
      

      【讨论】:

      • 如何检查文件是否存在。
      • 私有文件路径是什么?
      • 目的地在哪里使用?
      • 不适合我。尝试将privatePath_file_path 设置为路径并引发异常,因为参数包含路径分隔符:尝试仅放置文件名但失败,因为它查找/data/my.app.package/files/myFile(而它应该查找/storage/emulated/0/Android/data/my.app.package/files/myfile
      • 私有路径:data/user/0/com.mypakage.ui/files
      【解决方案5】:

      暂时的可能性:你可以在Android Manifest的<application>标签中使用android:requestLegacyExternalStorage="true"

      【讨论】:

      【解决方案6】:

      如果您使用 preserveLegacyExternalStorage,则旧版存储模型仅在用户卸载您的应用之前仍然有效。如果用户在运行 Android 11 的设备上安装或重新安装您的应用,那么无论 preserveLegacyExternalStorage 的值如何,您的应用都无法选择退出范围存储模型。

      使用此标志时要记住的事项。 know more

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-31
        • 2019-09-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-12-06
        • 2016-03-29
        • 2021-07-13
        相关资源
        最近更新 更多