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

12

我正在获取外部SD卡的PersistableUriPermission并将其存储以供进一步使用。 现在,当用户向我提供文件路径时,从我的应用程序中的文件列表中, 我想编辑该文档并将其重命名。

因此,我有要编辑的文件的文件路径。

我的问题是如何从我的TreeUri中获取该文件的Uri,以便编辑文件。

1个回答

23

访问SD卡中的文件

使用DOCUMENT_TREE对话框获取SD卡的Uri

通过图片或gif动画向用户展示如何在对话框中选择sd-card

// call for document tree dialog
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_OPEN_DOCUMENT_TREE);

onActivityResult 中,您将获得所选目录的 Uri。 (sdCardUri)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case REQUEST_CODE_OPEN_DOCUMENT_TREE:
            if (resultCode == Activity.RESULT_OK) {
                sdCardUri = data.getData();
             }
             break;
     }
  }

现在必须检查用户是否:
a. 选择了SD卡
b. 选择了我们的文件所在的SD卡(某些设备可能有多个SD卡)。
我们通过遍历层级,从sd卡根目录到我们的文件,检查a和b。如果找到了文件,则满足a和b的条件。
//First we get `DocumentFile` from the `TreeUri` which in our case is `sdCardUri`.
DocumentFile documentFile = DocumentFile.fromTreeUri(this, sdCardUri);

//Then we split file path into array of strings.
//ex: parts:{"", "storage", "extSdCard", "MyFolder", "MyFolder", "myImage.jpg"}
// There is a reason for having two similar names "MyFolder" in 
//my exmple file path to show you similarity in names in a path will not 
//distract our hiarchy search that is provided below.
String[] parts = (file.getPath()).split("\\/");

// findFile method will search documentFile for the first file 
// with the expected `DisplayName`

// We skip first three items because we are already on it.(sdCardUri = /storage/extSdCard)
for (int i = 3; i < parts.length; i++) {
    if (documentFile != null) {
        documentFile = documentFile.findFile(parts[i]);
    }
  }

if (documentFile == null) {

    // File not found on tree search
    // User selected a wrong directory as the sd-card
    // Here must inform the user about how to get the correct sd-card
    // and invoke file chooser dialog again.  

    // If the user selects a wrong path instead of the sd-card itself,  
    // you should ask the user to select a correct path.  
    // I've developed a gallery app with this behavior implemented in it.  
    // https://play.google.com/store/apps/details?id=com.majidpooreftekhari.galleryfarsi
    // After you installed the app, try to delete one image from the  
    // sd-card and when the app requests the sd-card, select a wrong path  
    // to see how the app behaves.  

 } else {

    // File found on sd-card and it is a correct sd-card directory
    // save this path as a root for sd-card on your database(SQLite, XML, txt,...)

    // Now do whatever you like to do with documentFile.
    // Here I do deletion to provide an example.


    if (documentFile.delete()) {// if delete file succeed 
        // Remove information related to your media from ContentResolver,
        // which documentFile.delete() didn't do the trick for me. 
        // Must do it otherwise you will end up with showing an empty
        // ImageView if you are getting your URLs from MediaStore.
        // 
        Uri mediaContentUri = ContentUris.withAppendedId(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                longMediaId);
        getContentResolver().delete(mediaContentUri , null, null);
    }


 }

我的应用程序在选择SD卡路径时出现错误的行为:

要检查错误的SD卡路径选择行为,请安装该应用程序并尝试删除存储在SD卡上的图像,然后选择错误的路径而不是您的SD卡目录。
日历画廊:https://play.google.com/store/apps/details?id=com.majidpooreftekhari.galleryfarsi

注意:

您必须在清单文件中提供对外部存储的访问权限,并在os>= Marshmallow内部提供应用程序访问权限。 https://dev59.com/qVwY5IYBdhLWcg3w0KnL#32175771


编辑SD卡文件

如果您想要调用其他应用程序来编辑SD卡上的现有图像,则无需执行以上步骤。

在此处,我们调用所有已安装应用程序中具有编辑图像能力的活动(程序员在清单中标记其应用程序的功能,以提供其他应用程序(活动)的可访问性)。

在您的editButton点击事件中:

String mimeType = getMimeTypeFromMediaContentUri(mediaContentUri);
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_EDIT).setDataAndType(mediaContentUri, mimeType).putExtra(Intent.EXTRA_STREAM, mediaContentUri).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION), "Edit"), REQUEST_CODE_SHARE_EDIT_SET_AS_INTENT);

这是获取 mimeType 的方法:

public String getMimeTypeFromMediaContentUri(Uri uri) {
    String mimeType;
    if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {
        ContentResolver cr = getContentResolver();
        mimeType = cr.getType(uri);
    } else {
        String fileExtension = MimeTypeMap.getFileExtensionFromUrl(uri
                .toString());
        mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
                fileExtension.toLowerCase());
    }
    return mimeType;
}

注意:

在Android KitKat(4.4)上不要求用户选择SD卡,因为在这个版本的Android中,DocumentProvider不适用,因此我们无法通过这种方法访问SD卡。 请查看DocumentProvider的API级别https://developer.android.com/reference/android/provider/DocumentsProvider.html
我找不到任何在Android KitKat(4.4)上有效的内容。如果您在KitKat上找到了有用的内容,请与我们分享。

在低于KitKat版本的系统中,已经提供了对SD卡的访问权限。


2
我认为你提供的代码是用来删除整个SD卡目录的。 - Ankesh kumar Jaisansaria
1
有比这个更快的方法吗? - Ankesh kumar Jaisansaria
1
如果用户选择了错误的路径而不是SD卡本身,则我在此提供的方法作为答案将返回false,并且您应该要求用户选择正确的路径。 我已经开发了一个带有此行为的画廊应用程序。 安装应用程序后,请尝试从SD卡中删除一张图像,当应用程序请求您的SD卡时,请选择错误的路径以查看应用程序的行为。 https://play.google.com/store/apps/details?id=com.majidpooreftekhari.dailygallery&hl=en_GB - Eftekhari
@Eftekhari,感谢您的回复,我看到了您的应用程序,它看起来很棒。我正在使用Moto G3。在这个设备上,它没有要求那个权限,但是在OnePlusOne上检查相同的事情时会要求权限。有没有简单的方法来检查是否需要此设备的权限?而不是在删除发生时得到错误。注意:如果您发现任何错误,请纠正它。谢谢。 - Palanivelraghul
1
@MSeiz5 当我们查询ContentResolver时,每个条目或记录都有一个ID。当我们想要删除一个条目时,我们需要提供该ID。在查询(搜索)时将ID保存在文件或数据库中。如果您想要性能,请不要保存在SharedPreferences中。 - Eftekhari
显示剩余23条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接