Android应用读取内部存储Download目录文件的权限配置问题
嘿,我来帮你理清读取Android Download目录的权限问题,顺便给你靠谱的代码参考!
首先得纠正一个小误解:/storage/emulated/0/Download(你写的路径少了storage前缀)属于外部存储的公共目录,不是内部存储哦~内部存储是每个App私有的专属目录,而这个是所有App都能访问的公共下载文件夹。
接下来分版本说权限配置和代码实现:
一、Manifest权限配置
根据你的Android目标版本,配置对应的权限:
1. Android 12及以下(API 31及更低)
只需要添加读取外部存储的权限,如果需要写入文件再加上写入权限:
<!-- 读取权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 写入权限(如果需要的话,限定在API32以下,因为13+有新权限) --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
2. Android 13及以上(API 32+)
如果只读取图片、视频、音频这类媒体文件,可以用更细分的权限;如果要读取所有类型的文件(比如文档、压缩包),还是保留READ_EXTERNAL_STORAGE:
<!-- 媒体文件读取权限 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- 读取所有文件的权限(如果需要) --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
二、运行时请求权限
因为这些都是危险权限,必须在App运行时向用户请求,不能只靠Manifest配置。举个Kotlin的例子(Java思路类似):
private val REQUEST_CODE_STORAGE = 1001 // 在合适的时机(比如页面启动、点击按钮时)检查并请求权限 fun checkStoragePermission() { val requiredPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Android13+,如果是媒体文件就用对应权限,这里以所有文件为例 Manifest.permission.READ_EXTERNAL_STORAGE } else { Manifest.permission.READ_EXTERNAL_STORAGE } if (ContextCompat.checkSelfPermission(this, requiredPermission) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(requiredPermission), REQUEST_CODE_STORAGE) } else { // 权限已拿到,开始读取Download目录 loadDownloadFiles() } } // 处理权限请求结果 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_CODE_STORAGE) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { loadDownloadFiles() } else { Toast.makeText(this, "需要存储权限才能访问下载文件夹哦", Toast.LENGTH_SHORT).show() } } }
三、读取Download目录的两种方式
方式1:直接访问目录(适用于Android 10+,仍支持公共目录访问)
private fun loadDownloadFiles() { val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) if (downloadDir.exists() && downloadDir.isDirectory) { val files = downloadDir.listFiles() files?.forEach { file -> // 这里可以处理每个文件,比如打印信息或者存入列表 Log.d("DownloadFiles", "文件名:${file.name},路径:${file.absolutePath}") } } else { Toast.makeText(this, "下载文件夹不存在", Toast.LENGTH_SHORT).show() } }
方式2:用MediaStore查询(更符合Android 10+分区存储的推荐方案)
如果你的App目标版本是Android 10+,用MediaStore查询更稳妥,避免目录访问的限制:
private fun queryDownloadFiles() { val projection = arrayOf( MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.DATA ) // 筛选Download目录下的文件 val selection = "${MediaStore.Files.FileColumns.RELATIVE_PATH} LIKE ?" val selectionArgs = arrayOf("%Download%") contentResolver.query( MediaStore.Files.getContentUri("external"), projection, selection, selectionArgs, null )?.use { cursor -> while (cursor.moveToNext()) { val fileName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME)) val filePath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA)) Log.d("DownloadFiles", "文件名:$fileName,路径:$filePath") } } }
最后提醒
如果你的App是文件管理器类的,需要访问所有文件,那Android 11+还可以申请MANAGE_EXTERNAL_STORAGE权限,但这个权限需要Google Play的特殊审核,一般App不建议用,优先用上面的方案就够了。
内容的提问来源于stack exchange,提问作者Michał Urban




