You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 13中SAF获取目录URI后无法访问文件的解决方案

解决Android 13下SAF目录URI的文件访问问题

首先明确:你拿到的content://com.android.externalstorage.documents/tree/primary%3ADocuments%2FSMART%2FFall_data这个URI格式是完全正确的,它是SAF提供的目录树URI,但这种URI不能用传统的File类或者直接查询目录本身来处理,这就是你两种方式报错的原因。

错误原因分析

  • 方式一错误:SAF的content URI不是文件系统路径,不能直接转成File对象,Android 13的存储权限模型更严格,直接操作File会完全失效。
  • 方式二错误:直接用目录的tree URI调用contentResolver.query()是不支持的,因为这个URI指向的是目录树节点,而非可查询的文档集合,需要构建专门的子文档查询URI。

正确实现步骤

1. 持久化目录权限(可选但推荐)

选完目录后,先申请持久化权限,避免APP重启后丢失访问权限:

if (data != null) {
    Uri directoryUri = data.getData();
    // 持久化权限
    getContentResolver().takePersistableUriPermission(
        directoryUri,
        Intent.FLAG_GRANT_READ_URI_PERMISSION
    );
    accessFilesInDirectory(directoryUri);
}

2. 正确查询目录下的JSON文件

使用DocumentsContract.buildChildDocumentsUriUsingTree构建查询子文件的URI,然后通过ContentResolver查询:

private void accessFilesInDirectory(Uri directoryUri) {
    ContentResolver contentResolver = getContentResolver();
    
    // 构建查询子文档的URI
    Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(
        directoryUri,
        DocumentsContract.getTreeDocumentId(directoryUri)
    );

    // 查询JSON文件(MIME类型为application/json)
    String[] projection = {
        DocumentsContract.Document.COLUMN_DOCUMENT_ID,
        DocumentsContract.Document.COLUMN_DISPLAY_NAME,
        DocumentsContract.Document.COLUMN_MIME_TYPE
    };
    String selection = DocumentsContract.Document.COLUMN_MIME_TYPE + " = ?";
    String[] selectionArgs = {"application/json"};

    Cursor cursor = null;
    try {
        cursor = contentResolver.query(childrenUri, projection, selection, selectionArgs, null);
        if (cursor != null && cursor.moveToFirst()) {
            do {
                String docId = cursor.getString(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DOCUMENT_ID));
                String fileName = cursor.getString(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME));
                Log.i("LOAD FILES:", "file name: " + fileName);

                // 构建单个文件的URI
                Uri fileUri = DocumentsContract.buildDocumentUriUsingTree(directoryUri, docId);
                // 读取文件内容
                readJsonFile(contentResolver, fileUri);
            } while (cursor.moveToNext());
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
}

3. 读取JSON文件内容

通过ContentResolver.openInputStream获取文件输入流,不要用FileInputStream:

private void readJsonFile(ContentResolver resolver, Uri fileUri) {
    BufferedReader reader = null;
    try {
        InputStream inputStream = resolver.openInputStream(fileUri);
        if (inputStream == null) return;
        
        reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder content = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            content.append(line);
        }
        // 这里处理JSON内容,比如转成对象
        Log.i("JSON CONTENT:", content.toString());
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

额外注意事项

  • Android 13及以上,SAF是访问共享存储的推荐方式,不要试图绕过SAF直接访问Documents目录,会被权限限制。
  • 如果需要操作子目录,递归查询的逻辑和上面类似,判断文档的MIME类型是否为DocumentsContract.Document.MIME_TYPE_DIR即可。

内容的提问来源于stack exchange,提问作者walt3rwhite

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 08:37:02