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

Android API 29无法读取Download目录文件问题求助

问题原因与解决办法

为什么看不到Download目录的文件?

你用的API29(Android 10)已经启用了分区存储(Scoped Storage),getExternalStoragePublicDirectory这个方法已被废弃,而且即便申请了READ_EXTERNAL_STORAGE权限,普通应用也无法直接读取公共Download目录里的非媒体类文件——系统会限制这种直接访问行为,所以才会出现目录存在但读不出文件的情况。

修复代码的几种方式

方式1:用MediaStore查询媒体文件(仅适用于图片/音频/视频等媒体类型)

如果目标文件是媒体类,可通过MediaStore查询:

fun queryMediaFiles() {
    val projection = arrayOf(
        MediaStore.Files.FileColumns._ID,
        MediaStore.Files.FileColumns.DISPLAY_NAME,
        MediaStore.Files.FileColumns.DATA
    )
    val selection = "${MediaStore.Files.FileColumns.MEDIA_TYPE} != ?"
    val selectionArgs = arrayOf(MediaStore.Files.FileColumns.MEDIA_TYPE_NONE.toString())
    val sortOrder = "${MediaStore.Files.FileColumns.DATE_ADDED} DESC"

    contentResolver.query(
        MediaStore.Files.getContentUri("external"),
        projection,
        selection,
        selectionArgs,
        sortOrder
    )?.use { cursor ->
        while (cursor.moveToNext()) {
            val fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME))
            // 处理找到的文件
        }
    }
}

方式2:用存储访问框架(SAF)让用户选择文件(通用所有文件类型)

这是官方推荐的通用方案,无需特殊权限,让用户手动选择目标文件:

private val REQUEST_CODE_PICK_FILE = 1001

fun openFilePicker() {
    val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
        addCategory(Intent.CATEGORY_OPENABLE)
        // 设置文件类型,所有文件用"*/*",也可指定如"text/plain"
        type = "*/*"
    }
    startActivityForResult(intent, REQUEST_CODE_PICK_FILE)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE_PICK_FILE && resultCode == Activity.RESULT_OK) {
        data?.data?.let { uri ->
            // 通过Uri读取文件内容,复制到应用本地存储
            contentResolver.openInputStream(uri)?.use { inputStream ->
                openFileOutput("target_file_name", Context.MODE_PRIVATE).use { outputStream ->
                    inputStream.copyTo(outputStream)
                }
            }
        }
    }
}

方式3:临时关闭分区存储(仅调试用,不推荐上线)

若只是临时调试,可在AndroidManifest.xml的<application>标签中添加:

android:requestLegacyExternalStorage="true"

这会暂时回到旧存储模式,让原有代码能读取Download目录文件,但该属性在Android 11及以上失效,且不符合官方存储规范,正式上线不建议使用。

替代的文件传输方案

除了邮件下载,还有这些更便捷的方式:

  • ADB推送:用命令adb push 本地文件路径 /sdcard/Download/直接将PC文件传到手机Download目录,再用上述SAF方式读取
  • 应用内网络传输:在应用中添加简单的HTTP服务或Socket功能,PC端通过网络直接把文件传到应用内部存储
  • 第三方工具中转:用QQ/微信将文件传到手机,再通过SAF在聊天目录中选择文件,复制到应用存储

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 08:04:58