【问题标题】:Is there any way of writing files to SDCard with flutter File.writeAsStringSync有什么方法可以使用颤振 File.writeAsStringSync 将文件写入 SDCard
【发布时间】:2020-09-21 17:40:22
【问题描述】:

我正在尝试写入位于 SDCard 中的文件,我发现我需要对可移动存储的特殊权限,这在任何已知的 Flutter 权限处理程序插件中都找不到(我尝试了 simple_permission 和 permission_handler 没有使用)。

我尝试使用 android 端获取这些权限,因此我编写了一个简单的函数来显示对话框,并且用户将允许应用修改 SDCard 的内容。

即使在获得 SDCARD 的权限后,在使用 File.writeAsStringSync 方法尝试将文件保存到 SDCard 时,我仍然会遇到相同的权限被拒绝错误。

我想知道是否有任何已知的方法/hack/解决方法可以将文件保存在 SDCards 中。

我使用的android代码与这个答案相同:https://stackoverflow.com/a/55024683/6641693

注意:我的目标是 android 7 及更高版本,但不是 android 11。

【问题讨论】:

  • 在使用过的设备上运行哪个 Android 版本?自 kitkat 以来,可移动 micro sd 卡是只读的。除了应用程序特定的目录。在 Android 11 上,您可以再次对某些目录进行写访问。
  • 使用 SAF,您可以对整张卡进行写访问。除非在 Android 11 下。
  • 您可以将文件保存到任何文件路径,前提是应用程序有权在其中保存文件。无需破解。
  • @Abion47 继续尝试,尝试将文件保存在具有存储权限的 sdCard 中,它不会起作用,您还需要 sdcard 权限。这里有一个完整的问题:github.com/flutter/flutter/issues/40504
  • @KakiMasterOfTime 该线程是关于将 Flutter 中的内置路径生成器包含到挂载 SD 卡的目录中。正如问题本身所述,SD 卡目录的位置因供应商而异,因此没有一种方法可以确定路径将是什么,因此该问题几乎被放弃了。你真的别无选择,只能检查所有常见的路径,看看它们是否存在。

标签: android flutter dart android-sdcard


【解决方案1】:

我通过放弃 dart 文件保存并使用 android SAF 解决了这个问题。 首先,我所做的是尝试获取 sdCard 修改权限。 之后,我就可以保存我需要的文件了。

这是我用来获取权限的代码(也就是“允许此应用修改您的 sdCard 上的内容”对话框)

public void takeCardUriPermission(String sdCardRootPath) {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
      File sdCard = new File(sdCardRootPath);
      StorageManager storageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
      StorageVolume storageVolume = storageManager.getStorageVolume(sdCard);
      Intent intent = storageVolume.createAccessIntent(null);
      try {
        startActivityForResult(intent, 4010);
      } catch (ActivityNotFoundException e) {
        Log.e("TUNE-IN ANDROID", "takeCardUriPermission: "+e);
      }
    }
  }

  protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == 4010) {

      Uri uri = data.getData();

      grantUriPermission(getPackageName(), uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
              Intent.FLAG_GRANT_READ_URI_PERMISSION);

      final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
              Intent.FLAG_GRANT_READ_URI_PERMISSION);

      getContentResolver().takePersistableUriPermission(uri, takeFlags);
      methodChannel.invokeMethod("resolveWithSDCardUri",getUri().toString());
    }
  }

  public Uri getUri() {
    List<UriPermission> persistedUriPermissions = getContentResolver().getPersistedUriPermissions();
    if (persistedUriPermissions.size() > 0) {
      UriPermission uriPermission = persistedUriPermissions.get(0);
      return uriPermission.getUri();
    }
    return null;
  }

所以为了开始整个权限获取过程,你必须首先调用takeCardUriPermission并传递sdCard路径的URI。

注意:在我的 FlutterActivity 上,我可以使用 getExternalCacheDirs()[1].toString() 直接获取 sdCardPath

调用takeCardUriPermission 后,一旦按下允许按钮(或拒绝),将调用活动结果事件并调用 onActivtyResult 方法。 requestCode 检查在您有多个事件并且需要过滤掉这一个时很有用。

活动结果代码将授予应用修改 sdCard 上文件的权限。

getUri 方法是我们之后尝试将字节保存到文件时使用的方法,它返回我们选择的 SDCard 的 URI(您可以有多个 sdCard)。

保存文件

我用来保存文件的方法很简单。首先我们需要获取 sdCard 的 URI 并从中创建一个Documentfile,然后我们通过该目录的层次结构(DocumentFile 可以引用文件和目录)根据它的 URI 找到所需的文件。

我们通过将文件 URI 拆分为多个部分来执行此搜索,然后通过测试每个部分是否存在来导航层次结构。一旦我们测试了所有部分,我们就会到达我们的文件,如果它存在,或者我们被困在最后一个目录。

此迭代的结果是一个DocumentFile,我们可以对其执行操作。

以下是完整的文件保存代码:

String filepath = (String) arguments.get("filepath");
          final byte[] bytes = methodCall.argument("bytes");

          try{
            if(filepath==null || bytes==null)throw new Exception("Arguments Not found");
            DocumentFile documentFile = DocumentFile.fromTreeUri(getApplicationContext(), getUri());
            String[] parts = filepath.split("/");
            for (int i = 0; i < parts.length; i++) {
              DocumentFile nextfile = documentFile.findFile(parts[i]); 
              if(nextfile!=null){
                documentFile=nextfile;
              }
            }
            if(documentFile!=null && documentFile.isFile()){
              OutputStream out = getContentResolver().openOutputStream(documentFile.getUri());
              out.write(bytes);
              out.close();
            }else{
              throw new Exception("File Not Found");
            }
          }catch (Exception e){
            result.error("400",e.getMessage(),e);
            return;
          }
          result.success(true);

注意:在我的代码中,我在 MethodChannel 的 MethodCallHandler 下调用它,这将给我我需要的参数:filePath 这是我要编写的文件的字符串 URI to 和 bytes 字节数组,表示我要保存的数据。 result.success

也可以这样说

文件写入代码很简单:打开文件,写入数据,关闭文件。

【讨论】:

    猜你喜欢
    • 2018-07-07
    • 2020-11-16
    • 1970-01-01
    • 1970-01-01
    • 2011-01-28
    • 1970-01-01
    • 1970-01-01
    • 2019-01-07
    • 1970-01-01
    相关资源
    最近更新 更多