存储访问框架获取正确的 Uri 路径删除/编辑/获取文件

Posted

技术标签:

【中文标题】存储访问框架获取正确的 Uri 路径删除/编辑/获取文件【英文标题】:Storage Access Framework Getting Correct Uri Path Delete/Edit/Get File 【发布时间】:2018-03-01 17:48:48 【问题描述】:

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

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

授予持久性权限 onActivityResult with:

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);

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

Uri treeUri = data.getData();
treeUri.toString()

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

File mediaStorageDir = new File(Environment.getExternalStorageDirectory(), "");

返回:storage/emulated/0

new File(treeUri.toString()).getAbsolutePath();

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

如果你使用 DocumentFile 类来获取你得到的主文件夹的路径

DocumentFile saveDir = null;
saveDir = DocumentFile.fromFile(Environment.getExternalStorageDirectory());
String uriString = saveDir.getUri().toString();

返回: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;
    

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

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

DocumentFile imageToDeletePath = DocumentFile.fromFile(new File(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);

imageDelete 返回 null,因为 Uri 格式不正确。

如果我打开 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;

检查字符串中的 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;

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

DocumentFile imageToDeletePath = DocumentFile.fromTreeUri(this, Uri.parse(lastSavedImagePath));
DocumentFile imageToDelete = imageToDeletePath.findFile(lastSavedImageName);

【问题讨论】:

【参考方案1】:

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

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

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

另见: Android - Storage Access Framework - Uri into local file

【讨论】:

以上是关于存储访问框架获取正确的 Uri 路径删除/编辑/获取文件的主要内容,如果未能解决你的问题,请参考以下文章

Android SAF(存储访问框架):从 TreeUri 获取特定文件 Uri

使用其完整路径作为URI从隔离存储中访问文件

Android:从存储访问框架获得的 URI 中使用意图选择器打开文件

使用 KitKat 存储访问框架后打开 Google Drive File Content URI

获取存储在 sd 卡 + android 中的图像的缩略图 Uri/路径

如何获取原始文件夹uri路径并保存在android的内部存储器中?