首页 > 解决方案 > Android SAF,无法复制文件,未设置 FLAG_SUPPORTS_COPY

问题描述

我正在尝试使用DocumentsContract.copyDocument(ContentResolver, Uri, Uri)在 Android 中使用 SAF 框架复制文档,但这不起作用,android 返回错误“无法复制文档”。

通过缩小问题范围,该文档FLAG_SUPPORTS_COPY关闭(根据DocumentsContract.Document#COLUMN_FLAGS)(COLUMN_FLAGS 值是十进制的 326)。所以这解释了错误。

但是允许移动文档(标志打开)并且在调用时文件确实被移动FLAG_SUPPORTS_MOVEDocumentsContract.moveDocument(ContentResolver, Uri, Uri, Uri)

已通过以下方式授予对文档树(驱动器根目录和 DCIM 文件夹)的访问权限Intent.ACTION_OPEN_DOCUMENT_TREE

为什么FLAG_SUPPORTS_COPY将 Document 设置为 false ?我错过了什么吗?

注意:我相信我满足了这篇文章的要求https://stackoverflow.com/a/58147682/15401262

谢谢

代码(Java)

// docFilesToProcess if of type "DocumentFile[]" and contains "regular files, like images" (not directories).
// Create destination dir
Uri destUri = DocumentsContract.createDocument(this.getContentResolver(), docFilesToProcess[i].getParentFile().getUri(), DocumentsContract.Document.MIME_TYPE_DIR, "destDir");
Log.i("M", "destUri: "+ destUri.toString());
// Create document
Uri docToMove = DocumentsContract.createDocument(this.getContentResolver(), docFilesToProcess[i].getParentFile().getUri(), "text/plain", "text");
Log.i("M", "docToMove: "+ docToMove.toString());
// copy document
DocumentsContract.copyDocument(this.getContentResolver(), docToMove, destUri);

输出

I/M: destUri: content://com.android.externalstorage.documents/tree/primary%3ADCIM/document/primary%3ADCIM%2FdestDir
I/M: docToMove: content://com.android.externalstorage.documents/tree/primary%3ADCIM/document/primary%3ADCIM%2Ftext.txt
W/DocumentsContract: Failed to copy document
    java.lang.UnsupportedOperationException: Copy not supported
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:172)
        at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
        at android.content.ContentProviderProxy.call(ContentProviderNative.java:658)
        at android.content.ContentResolver.call(ContentResolver.java:2042)
        at android.provider.DocumentsContract.copyDocument(DocumentsContract.java:1442)
        at com.example.exifthumbnailadder.MainActivity.addThumbs(MainActivity.java:1036)
        at java.lang.reflect.Method.invoke(Native Method)

持久权限请求

    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    intent.addFlags(
        Intent.FLAG_GRANT_READ_URI_PERMISSION
            | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
            | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);

标签: androidandroid-intenturiandroid-permissionsintentservice

解决方案


通常,您永远不能依赖任何 SAF 提供程序来实现任何可选功能。您应该检查该功能是否受支持,并且您应该有一个可行的后备计划,以应对不支持的情况。

ContentResolver cr = getContentResolver();
if((flags & FLAG_SUPPORTS_MOVE) == FLAG_SUPPORTS_MOVE)
    Uri newDoc = DocumentsContract.copyDocument(cr, docToCopy, destDir);
else {
    Uri newDoc = DocumentsContract.createDocument(cr, destDir, mimeType, name);
    manuallyCopyBytes(docToCopy, newDoc);
}

给提供商一个机会这样做很重要,因为像 Google Drive 这样的东西可能能够在设备和服务器上分别执行复制,而不是在设备上复制它然后必须将整个内容上传到再次服务器。


推荐阅读