Android - 获取DocumentFile,对sd卡上的任何文件路径具有写访问权限(已获得sd卡权限)

时间:2016-03-27 00:03:16

标签: android android-sdcard documentfile

在我的应用程序中,我使用以下意图获得SD卡写入权限。如果用户从系统文件资源管理器中选择sd卡文件夹,那么我有SD卡写访问权限。

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.putExtra("android.content.extra.SHOW_ADVANCED", true);
startActivityForResult(intent, 42);

之后我可以使用DocumentFile类修改SD卡中的文件。但是我在为随机文件路径获取DocumentFile时遇到了问题。

Document.fromFile(new File(path));
Document.fromSingleUri(Uri.fromFile(new File(path)));

都返回一个在.ca​​nWrite()上返回false的DocumentFile对象。即使我已经获得SD卡许可。

所以我编写了在我的问题末尾发布的方法,以获得一个在.ca​​nWrite()上返回true的DocumentFile。但这很慢......而且感觉非常错误!必须有更好的方法来做到这一点。我还写了一个返回与

相同的字符串的方法
String docFileUriString = docFile.getUri().toString(); 

对于任何文件,其中docFile是由下面的方法返回的DocumentFile。但是

DocumentFile.fromTreeUri(Uri.parse(docFileUriString ));

返回一个DocumentFile,它指向sd卡的根,而不是DocumentFile路径。这很奇怪。有人可以提出更优雅的解决方案吗?

public static DocumentFile getDocumentFileIfAllowedToWrite(File file, Context con){ 

    List<UriPermission> permissionUris = con.getContentResolver().getPersistedUriPermissions();

    for(UriPermission permissionUri:permissionUris){

        Uri treeUri = permissionUri.getUri();
        DocumentFile rootDocFile = DocumentFile.fromTreeUri(con, treeUri);
        String rootDocFilePath = FileUtil.getFullPathFromTreeUri(treeUri, con);

        if(file.getAbsolutePath().startsWith(rootDocFilePath)){

            ArrayList<String> pathInRootDocParts = new ArrayList<String>();
            while(!rootDocFilePath.equals(file.getAbsolutePath())){
                pathInRootDocParts.add(file.getName());
                file = file.getParentFile();
            } 

            DocumentFile docFile = null;  

            if(pathInRootDocParts.size()==0){ 
                docFile = DocumentFile.fromTreeUri(con, rootDocFile.getUri()); 
            }
            else{
                for(int i=pathInRootDocParts.size()-1;i>=0;i--){
                    if(docFile==null){docFile = rootDocFile.findFile(pathInRootDocParts.get(i));}
                    else{docFile = docFile.findFile(pathInRootDocParts.get(i)); }  
                }
            }
            if(docFile!=null && docFile.canWrite()){ 
                return docFile; 
            }else{
                return null;
            }

        }
    }
    return null; 
}

2 个答案:

答案 0 :(得分:1)

Document.fromFile(File) 在最近的设备上似乎无法为此目的正常工作

这是答案的链接,我获得了有关该问题的最多信息 https://stackoverflow.com/a/26765884/971355

关键步骤是:

  1. 获得对 SD 卡的写访问权限,并使其在手机重启后保持不变

    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {

    ...

    getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

    }

  2. 之后,无论文件和文件夹嵌套多深,您都可以访问它们,但您必须了解您必须处理的复杂 URL。

如果你像我一样是一个 linux 人,并且曾经认为一切都是一个文件,一切都很简单。这次不是。 SD 卡上的文件,例如 /storage/13E5-2604/testDir/test.txt 其中 13E5-2604 是您的 SD 卡根文件夹:

content://com.android.externalstorage.documents/tree/13E5-2604%3A/document/13E5-2604%3A%2FtestDir%2Ftest.txt

新现实中。为什么他们做得这么复杂 - 是另一个问题......

我不知道有一种 API 方法可以透明且轻松地将普通的 linux 路径转换为这个新现实路径。但是在您完成之后(%3A 是 : 和 %2F 是/参见 https://www.w3schools.com/tags/ref_urlencode.ASP 以供参考)您可以轻松创建 DocumentFile 的实例:

DocumentFile txtFile = DocumentFile.fromSingleUri(context,
Uri.parse("content://com.android.externalstorage.documents/tree/13E5-2604%3A/document/13E5-2604%3A%2FtestDir%2Ftest.txt"));

并写信给它。

答案 1 :(得分:0)

回应@AndiMar(3月21日),我只是检查它是否存在于内部存储器上,如果它不需要担心。

       try {
        if (sourcefile.toString().contains(Environment.getExternalStorageDirectory().toString())) {
            if (sourcefile.exists()) {
                sourcefile.delete();
            }
        } else {
            FileUtilities fio = new FileUtilities();
            DocumentFile filetodelete = fio.getDocumentFileIfAllowedToWrite(sourcefile, context);
            if (filetodelete.exists()) {
                filetodelete.delete();
            }
        }

    } catch (Exception e) {
        e.printStackTrace();

    }