有什么方法可以用 flutter 将文件写入 SDCard File.writeAsStringSync

Is there any way of writing files to SDCard with flutter File.writeAsStringSync

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

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

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

想知道有没有什么已知的way/hack/workaround可以在flutter中保存文件到SD卡

我使用的 android 代码与此答案相同:

注意: 我的目标是 android 7 及以后,但不是 android 11.

我通过放弃保存 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 后,一旦按下允许按钮(或拒绝),将调用 activity 结果事件并调用 onActivtyResult 方法。 requestCode 检查在您有多个事件并且需要过滤掉这个事件时很有用。

activity 结果代码将授予应用程序修改 sdCard 上文件的权限。

getUri 方法是我们稍后尝试将字节保存到文件时使用的方法,它 returns 我们选择的 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);

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

也可以这样说

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