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

Android 13及以后读写外置SD卡任意文件的方法与权限咨询

Android 13及更高版本外置SD卡读写操作指南

一、读取任意位置的任意文件

Android 13+的存储权限模型大幅收紧,*READ_MEDIA_系列权限仅针对图片、视频、音频这类媒体文件,无法读取txt、html等非媒体文件,也无法访问SD卡上的任意目录。要实现任意位置文件读取,有两种可行方案:

方案1:存储访问框架(SAF)——推荐(符合Google规范)

这是官方推荐的方式,通过让用户手动选择要访问的文件或目录来获取权限:

  • 无需申请传统存储权限,只需通过Intent唤起系统文件选择器:
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
    startActivityForResult(intent, REQUEST_CODE_PICK_DIR)
    
  • 在onActivityResult(或Activity Result API)中获取用户选择的目录Uri,后续用DocumentFile类遍历目录、读取文件:
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE_PICK_DIR && resultCode == RESULT_OK) {
            data?.data?.let { treeUri ->
                // 持久化权限,避免重启后丢失
                contentResolver.takePersistableUriPermission(
                    treeUri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION
                )
                // 遍历目录下的文件
                val rootDir = DocumentFile.fromTreeUri(this, treeUri)
                rootDir?.listFiles()?.forEach { file ->
                    // 获取文件名、类型等信息
                    val fileName = file.name
                    val mimeType = file.type
                }
                // 读取文件内容
                val inputStream = contentResolver.openInputStream(file.uri)
                val content = inputStream?.bufferedReader().use { it?.readText() }
            }
        }
    }
    
  • 针对Downloads文件夹的非媒体文件:也可以用ACTION_OPEN_DOCUMENT让用户直接选择该文件,或者用SAF选择Downloads目录,流程和上面一致。

方案2:MANAGE_EXTERNAL_STORAGE特殊权限——仅适用于文件管理器类应用

这个权限可以获取全存储访问权,但Google Play审核严格,仅允许文件管理器、备份类等核心功能需要的应用申请:

  • 在Manifest中声明权限:
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
                     android:minSdkVersion="30" />
    
  • 动态申请权限:
    if (!Environment.isExternalStorageManager()) {
        val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
        intent.data = Uri.parse("package:$packageName")
        startActivity(intent)
    }
    
  • 获得权限后,就可以用传统的File类操作SD卡任意位置的文件,但注意这种方式不符合现代存储规范,非必要不推荐。

二、向外置SD卡任意位置写入文件

和读取逻辑一致,核心也是两种方案:

方案1:存储访问框架(SAF)——推荐

  • 让用户选择要写入的目录(或直接创建文件):
    // 创建新文件
    val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        type = "text/plain" // 根据文件类型设置MIME
        putExtra(Intent.EXTRA_TITLE, "new_file.txt")
    }
    startActivityForResult(intent, REQUEST_CODE_CREATE_FILE)
    
  • 获取文件Uri后写入内容:
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE_CREATE_FILE && resultCode == RESULT_OK) {
            data?.data?.let { fileUri ->
                contentResolver.openOutputStream(fileUri)?.use { outputStream ->
                    outputStream.write("写入的内容".toByteArray())
                }
            }
        }
    }
    
  • 如果是向已存在的文件写入,用ACTION_OPEN_DOCUMENT获取文件Uri,加上FLAG_GRANT_WRITE_URI_PERMISSION权限,再打开输出流写入。

方案2:MANAGE_EXTERNAL_STORAGE特殊权限

和读取的方案2一致,获得权限后用File类直接写入SD卡任意位置,同样仅适用于少数合规应用。

三、示例:开发文件浏览+查看/打开应用

核心流程如下:

  1. 唤起SAF选择目录:启动ACTION_OPEN_DOCUMENT_TREE让用户选择SD卡上的目标文件夹,持久化权限。
  2. 遍历文件夹文件:用DocumentFile.fromTreeUri获取目录对象,调用listFiles()遍历所有文件/子目录,展示文件名、大小、类型等信息。
  3. 查看文件内容:点击文件时,用contentResolver.openInputStream(uri)读取文件内容,根据MIME类型选择TextView(文本、html)或其他控件展示。
  4. 打开文件:调用系统应用打开文件,用ACTION_VIEWIntent:
    fun openFile(fileUri: Uri, mimeType: String?) {
        val intent = Intent(Intent.ACTION_VIEW).apply {
            setDataAndType(fileUri, mimeType)
            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        }
        startActivity(intent)
    }
    

注意事项:

  • 必须持久化SAF权限(takePersistableUriPermission),否则应用重启后会丢失访问权。
  • 处理不同MIME类型的文件时,要正确设置Intent的type参数,避免无法打开。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 16:27:36