Android应用读取内部存储Download目录文件的权限配置问题
嘿,我来帮你理清读取Android Download目录的权限问题,顺便给你靠谱的代码参考!
首先得纠正一个小误解:
/storage/emulated/0/Download
(你写的路径少了
storage
前缀)属于
外部存储的公共目录
,不是内部存储哦~内部存储是每个App私有的专属目录,而这个是所有App都能访问的公共下载文件夹。
接下来分版本说权限配置和代码实现:
根据你的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()
方式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,
)?.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))