Storage Access Framework获取正确的Uri路径删除/编辑/获取文件

Thr*_*ian 6 android storage-access-framework documentfile

TL:DR; 我解释了如何使用DocumentFile使用创建文件夹和子文件夹以及如何删除使用此类创建的文件。从onActvityResult()和documentFile.getUri.toString()返回的Uri不相同。我的问题是如何在不使用SAF UI的情况下获得有效的Uri来操作文件夹和文件,如果可能的话,不使用hack也不行。

让我分享到目前为止我学到的知识并提出我的问题。如果要获取文件夹的Uri并对其进行处理,则应使用Intentwith ACTION_OPEN_DOCUMENT_TREE获取一个Uri来访问文件夹并为该uri设置W / R权限。

通过以下方式授予 onActivityResult的持久权限

final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
Run Code Online (Sandbox Code Playgroud)

如果选择设备的主文件夹:

Uri treeUri = data.getData();
treeUri.toString()
Run Code Online (Sandbox Code Playgroud)

返回:content://com.android.externalstorage.documents/tree/primary:

File mediaStorageDir = new File(Environment.getExternalStorageDirectory(), "");
Run Code Online (Sandbox Code Playgroud)

返回值:存储/模拟/ 0

new File(treeUri.toString()).getAbsolutePath();
Run Code Online (Sandbox Code Playgroud)

返回值:内容:/com.android.externalstorage.documents/tree/primary:

如果使用DocumentFile类获取主文件夹的路径,则会得到

DocumentFile saveDir = null;
saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
String uriString = saveDir.getUri().toString();
Run Code Online (Sandbox Code Playgroud)

返回值:file:/// storage / emulated / 0

我的第一个问题是如何使用DocumentFile类获取带有内容的Uri。

我正在构建一个摄影应用程序,默认情况下,我想使用DocumentFile类为图像设置初始文件夹。

 @TargetApi(19)
protected DocumentFile getSaveDirMainMemory() {
    DocumentFile saveDir = null;
    saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
    // saveDir =
    // DocumentFile.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));
    // saveDir =
    // DocumentFile.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));

    DocumentFile newDir = null;
    /*
     * Check or create Main Folder
     */

    // Check if main folder exist
    newDir = saveDir.findFile(DIR_MAIN);

    // Folder does not exist, create it
    if (newDir == null || !newDir.exists()) {
        newDir = saveDir.createDirectory(DIR_MAIN);
    }
    /*
     * Check or create Sub-Folder
     */
    DocumentFile newSubDir = null;

    // Check if sub-folder exist
    newSubDir = newDir.findFile(DIR_SUB);


    // Folder does not exist, create it
    if (newSubDir == null || !newSubDir.exists()) {
        newSubDir = newDir.createDirectory(DIR_SUB);
    }

    if (newSubDir != null && newSubDir.exists()) {
        return newSubDir;
    } else if (newDir != null && newDir.exists()) {
        return newDir;
    } else {
        return saveDir;
    }
}
Run Code Online (Sandbox Code Playgroud)

此方法根据选择在设备的主存储器或PICTURES或DCIM文件夹内创建DIR_MAIN / DIR_SUB。使用此默认文件夹,我将图像保存到此创建的子文件夹中。我得到了newSubDir.getUri()。toString():file:/// storage / emulated / 0 / MainFolder / SubFolder我命名为DIR_MAIN MainFolder,DIR_SUB:要测试的SubFolder。

要访问或删除图像,我使用此路径和创建为的图像名称

DocumentFile imageToDeletePath = DocumentFile.fromFile(new File(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);
Run Code Online (Sandbox Code Playgroud)

由于Uri格式不正确,因此imageDelete返回null。

如果我打开SAF ui并获取UI onActivityResult并将其另存为字符串,则使用此方法获取目录并检查Uri权限

@TargetApi(19)
protected DocumentFile getSaveDirNew(String uriString) {
    DocumentFile saveDir = null;

    boolean canWrite = isUriWritePermission(uriString);

    if (canWrite) {
        try {
            saveDir = DocumentFile.fromTreeUri(MainActivity.this, Uri.parse(uriString));
        } catch (Exception e) {
            saveDir = null;
        }
    }

    return saveDir;
}
Run Code Online (Sandbox Code Playgroud)

检查字符串中的Uri是否具有写权限,如果您不获取或删除持久性权限,则可能没有。

private boolean isUriWritePermission(String uriString) {
    boolean canWrite = false;

    List<UriPermission> perms = getContentResolver().getPersistedUriPermissions();
    for (UriPermission p : perms) {
        if (p.getUri().toString().equals(uriString) && p.isWritePermission()) {
            Toast.makeText(this, "canWrite() can write URI::  " + p.getUri().toString(), Toast.LENGTH_LONG).show();
            canWrite = true;
            break;
        }
    }
    return canWrite;
}
Run Code Online (Sandbox Code Playgroud)

使用有效的uri保存图像并使用后

DocumentFile imageToDeletePath = DocumentFile.fromTreeUri(this, Uri.parse(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);
Run Code Online (Sandbox Code Playgroud)

dar*_*ken 1

Uri.fromFile()DocumentFile.fromTreeUri()从两个不同的世界创建 uri。

虽然它们目前看起来非常相似,但这只是巧合,并且可能会随着未来的 Android 版本而改变。

不存在从一种方式转换为另一种方式的“非hacky”方式。如果你想要一个肮脏的解决方案,你可以去反射(查看源代码DocumentFile.fromTreeUri,并可能Storage在较新的 Android 版本上使用该类。

另请参阅: Android - 存储访问框架 - Uri 到本地文件