【问题标题】:Storage Access Framework - failing to obtain document tree from uri (returned from Drive app)存储访问框架 - 无法从 uri 获取文档树(从 Drive 应用程序返回)
【发布时间】:2019-05-31 10:31:44
【问题描述】:

我的 Android 应用想要在 Google Drive 中创建一个文件夹并从设备上的 Drive 应用获取 uri。

它发送一个intent,你可以看下面的代码:

private void createFolder(String folderName) {
    Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    // Create a file with the requested MIME type.
    intent.setType("vnd.android.document/directory");
    intent.putExtra(Intent.EXTRA_TITLE, folderName);
    startActivityForResult(intent, WRITE_REQUEST_CODE);
}

然后获取返回的数据,可以看下面的代码:

@Override
protected void  onActivityResult(int requestCode, int resultCode, Intent resultData)
{

    if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {

        Uri uri = null;
       if (resultData != null) {
            uri = resultData.getData();
            Log.i("result", "Uri: " + uri.toString());
            DocumentFile pickedDir = DocumentFile.fromTreeUri(this, uri); //this line gives the error
                       }
    }

}

我使用了一种在 Internet 上找到的 mimetype,它似乎是一种标准类型。使用“application/vnd.google-apps.folder”时也会出现同样的错误。 目标是拥有一个文档树,但在 logcat 中我收到以下错误:

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=43, result=-1, data=Intent { dat=content://com.google.android.apps.docs.storage/document/acc=1;doc=encoded=LongStringWithRandomCharactersHere/j flg=0x43 }} to activity {com.example.myapplication/com.example.myapplication.MainActivity}: java.lang.IllegalArgumentException: Invalid URI: content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3Dencoded%3DSameLongStringWithRandomCharactersHere%2Fj

及以下

Caused by: java.lang.IllegalArgumentException: Invalid URI: content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3Dencoded%3DAnotherLongStringWithDifferentRandomDataHere

这是完整的堆栈跟踪:

2019-05-31 15:06:50.167 31184-31184/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 31184
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=43, result=-1, data=Intent { dat=content://com.google.android.apps.docs.storage/document/acc=1;doc=encoded=randomcharacters flg=0x43 }} to activity {com.example.myapplication/com.example.myapplication.MainActivity}: java.lang.IllegalArgumentException: Invalid URI: content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3Dencoded%3Drandomcharacters
    at android.app.ActivityThread.deliverResults(ActivityThread.java:4365)
    at android.app.ActivityThread.handleSendResult(ActivityThread.java:4409)
    at android.app.ActivityThread.-wrap19(Unknown Source:0)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1670)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6687)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810)
 Caused by: java.lang.IllegalArgumentException: Invalid URI: content://com.google.android.apps.docs.storage/document/acc%3D1%3Bdoc%3Dencoded%3Drandomcharacters
    at android.provider.DocumentsContract.getTreeDocumentId(DocumentsContract.java:1023)
    at android.support.v4.provider.DocumentFile.fromTreeUri(DocumentFile.java:138)
    at com.example.myapplication.MainActivity.onActivityResult(MainActivity.java:254)
    at android.app.Activity.dispatchActivityResult(Activity.java:7295)
    at android.app.ActivityThread.deliverResults(ActivityThread.java:4361)
    at android.app.ActivityThread.handleSendResult(ActivityThread.java:4409) 
    at android.app.ActivityThread.-wrap19(Unknown Source:0) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1670) 
    at android.os.Handler.dispatchMessage(Handler.java:106) 
    at android.os.Looper.loop(Looper.java:164) 
    at android.app.ActivityThread.main(ActivityThread.java:6687) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:810) 

uri 没有错,因为我可以进一步提取在云端硬盘上成功创建的文件夹名称(并且大小=0)。 相反有什么问题?看来我的应用程序无法将其作为文档树处理。

【问题讨论】:

  • “怎么了?” -- ACTION_CREATE_DOCUMENT 用于创建文档,而不是树。没有要求任何DocumentsProvider 以您期望的方式尊重该 MIME 类型。 “看来我的应用程序无法将其作为文档树处理”——存储访问框架大概将其视为文档,因为这是您通过ACTION_CREATE_DOCUMENT 要求创建的。
  • @CommonsWare "document" 在 SAF 行话中也是一个文件夹,实际上,一个文档可以在其级别下有其他文档。至少云端硬盘应用对此感到满意或容忍它,因为我看到文件夹已创建。
  • 这并不意味着存储访问框架中间件会按照您认为的方式工作。
  • @CommonsWare 好的,但是要请求什么操作(对于文字游戏很抱歉)以使中间件满意?
  • @CommonsWare 如果我使用 ACTION_OPEN_DOCUMENT_TREE Android 说没有应用程序能够处理意图(驱动器无法打开),所以我认为它没有实现(我在设备上有其他云应用程序,即使使用 createChooser 选项,它们也不会出现)

标签: java android uri directory-structure storage-access-framework


【解决方案1】:

这适用于 Google Drive 云空间,但它可能是一个 hack。不能保证始终有效。

此示例在用户创建的云文件夹中创建云文件(SAF 选择器由应用打开)。

创建云文件夹(用户必须先从选择器 UI 中选择云根目录)

 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
 intent.setType("vnd.android.document/directory");
 intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
 startActivityForResult(intent, CREATE_DIRECTORY_REQUEST_CODE);

onActivityResult

@Override
protected void onActivityResult(int requestCode,int resultCode, Intent data) 
{
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == CREATE_DIRECTORY_REQUEST_CODE) 
    {
      if (resultCode == Activity.RESULT_OK) {
      int takeFlags = data.getFlags()
                   &
      (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
      ContentResolver resolver = this.getContentResolver();
      resolver.takePersistableUriPermission(data.getData(),takeFlags);
      archiveUri(data.getData().toString()); //important to save the toString() result, not getPath()
      archiveAuthority(data.getData().getAuthority());
      } 
      else 
      {
            // The user cancelled the request.
      }
    }
}

下次使用 Uri

ContentResolver contentResolver;
contentResolver = this.getContentResolver();
uriPath=retrieveArchivedUri();
Uri tempUri;
Uri uri; 
String authority;
tempUri=Uri.parse(uriPath);
authority=retrieveArchivedAuthority();
uri= DocumentsContract.buildDocumentUri(authority,
            DocumentsContract.getDocumentId(tempUri)); //folder Uri

try 
{
 DocumentsContract.createDocument(contentResolver,uri,"plain/text","fileName");
} 
catch (FileNotFoundException e) 
{
        e.printStackTrace();
}

【讨论】:

  • @CommonsWare 也许你有兴趣看到这个
  • 这很有趣。但是创建后如何写入该文件?我似乎收到了权限被拒绝错误
  • @m33ts4k0z 你的意思是你尝试了我的代码但它没有工作,或者你认为它给出了错误?标志非常重要,问题主要在于意图。然而,这是一个 hack,我现在可以看到 SAF 中间件不喜欢它或者存在错误(因为理论上意图的行为在逻辑上是连贯的),
  • 您的代码有效,它会创建一个空文件。但是如何附加到该文件?
  • 我试图写入使用生成的 URI 创建的文件,但我得到权限拒绝: var new_file = DocumentsContract.createDocument(contentResolver,uri,"plain/text","fileName"); contentresolve.openFileDescriptor(new_file, "w")?.use { val pathToNewFile = FileOutputStream(it.fileDescriptor) val inFile = context.openFileInput(file).readBytes() context.openFileOutput(file, Context.MODE_PRIVATE).use { pathToNewFile .write(inFile) } File(absolutePath + File.separator + file).delete() }
猜你喜欢
  • 1970-01-01
  • 2016-12-27
  • 1970-01-01
  • 2014-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-07
  • 2013-12-02
相关资源
最近更新 更多