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

Posted

技术标签:

【中文标题】Android SAF(存储访问框架):从 TreeUri 获取特定文件 Uri【英文标题】:Android SAF (Storage Access FrameWork): Get particular file Uri from TreeUri 【发布时间】:2016-12-27 12:35:23 【问题描述】:

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

所以我有了要编辑的文件的文件路径。

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

【问题讨论】:

【参考方案1】:

访问 SD 卡的文件

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

告知用户如何在对话框中选择sd-card。 (附图片或gif动画)

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

现在必须检查用户是否,

一个。选择了sd卡

b.选择我们的文件所在的 sd 卡(某些设备可能有多个 sd 卡)。


我们通过从 sd root 到我们的文件的层次结构查找文件来检查 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://***.com/a/32175771/2123400


编辑 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-card,因为在这个版本的 Android 上DocumentProvider 不适用,因此我们没有机会通过这种方法访问 sd-card . 查看DocumentProvider 的 API 级别 https://developer.android.com/reference/android/provider/DocumentsProvider.html 我找不到任何适用于 Android KitKat(4.4) 的东西。如果您发现任何对 KitKat 有用的信息,请与我们分享。

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

【讨论】:

我不想删除文件 我认为你提供的代码我删除了整个 SD 卡目录。 有没有比这个更快的方法 @Palanivelraghul 如果用户选择了错误的路径而不是 sd 卡本身,我在此处提供的作为答案的方法将返回 false,并且返回 false,您应该要求用户选择一个正确的路径。我开发了一个画廊应用程序,其中实现了这种行为。安装应用程序后,尝试从 sd 卡中删除一张图像,当应用程序请求您的 sd 卡时,选择错误的路径以查看应用程序的行为。 play.google.com/store/apps/… @MSeiz5 当我们查询ContentResolver 时,每个条目或记录都有一个ID。当我们想删除一个条目时,我们需要提供 id。在查询(搜索)时,将 id 保存在文件或数据库中。如果您想要性能,请不要保存在SharedPreferences

以上是关于Android SAF(存储访问框架):从 TreeUri 获取特定文件 Uri的主要内容,如果未能解决你的问题,请参考以下文章

使用 SAF(存储访问框架)的 Android SD 卡写入权限

SAF(存储访问框架)是不是解决了 Android 4.4(KitKat)中的 SD 卡 WRITE 问题?

额外启用带有存储访问框架 (SAF) 的显示/隐藏 SD 卡

如何使用android存储访问框架正确覆盖文件内容

在 Android 中从 File Api 迁移到 Storage Access Framework (SAF)

SAF(Storage Access Framework)使用攻略