Android(Kotlin)中DownloadManager路径错误及默认缓存文件获取问题
DownloadManager路径问题及解决方案
一、路径错误的原因
DownloadManager是系统级服务,运行在独立进程中,没有权限访问你的App私有内部存储目录(即getDir()返回的/data/data/包名/app_xxx路径)。当你尝试将下载目标设置到这个目录时,就会触发SecurityException。
二、正确设置目标文件的方式
推荐两种合法的存储路径选择:
1. 保存到App专属外部存储目录
这个目录属于App私有空间,其他App无法访问,App卸载时会自动删除,Android 4.4+无需额外权限:
val urlStr = "https://example.net/SomeFolder/SomeFile.mp3" // 直接指定App专属外部存储的Download子目录 val request = DownloadManager.Request(Uri.parse(urlStr)) .setDestinationInExternalFilesDir( this, Environment.DIRECTORY_DOWNLOADS, "downloadAudio.mp3" ) val dldMgr = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager downloadID = dldMgr.enqueue(request)
也可以手动构建文件对象后设置:
val dldFile = File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "downloadAudio.mp3") val request = DownloadManager.Request(Uri.parse(urlStr)) .setDestinationUri(Uri.fromFile(dldFile))
2. 保存到公共外部存储目录
如果需要让文件在系统“下载”文件夹中可见,需根据系统版本申请对应存储权限:
// Android 9及以下需提前申请WRITE_EXTERNAL_STORAGE权限 val request = DownloadManager.Request(Uri.parse(urlStr)) .setDestinationInExternalPublicDir( Environment.DIRECTORY_DOWNLOADS, "downloadAudio.mp3" ) // 可选:通知栏显示下载进度和完成提示 .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
三、获取默认共享下载缓存的文件及访问方法
未设置目标路径时,DownloadManager会将文件保存到系统共享下载目录(通常是/storage/emulated/0/Download/),可通过DownloadManager.Query查询文件信息:
// 下载完成后查询文件详情 fun getDownloadedFileInfo(downloadID: Long) { val dldMgr = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val query = DownloadManager.Query().setFilterById(downloadID) val cursor = dldMgr.query(query) if (cursor.moveToFirst()) { val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) if (status == DownloadManager.STATUS_SUCCESSFUL) { // 获取文件URI val localUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)) // 获取文件名 val fileName = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE)) // 获取文件总大小 val fileSize = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) // 访问文件:通过ContentResolver打开输入流 val inputStream = contentResolver.openInputStream(Uri.parse(localUri)) // 转为File对象(仅当URI为file://协议时有效) val filePath = Uri.parse(localUri).path val file = filePath?.let { File(it) } } } cursor.close() }
注意:共享目录中的文件不会随App卸载删除,且其他App可访问。
内容的提问来源于stack exchange,提问作者Michel
相关产品推荐
相关产品推荐

