You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动