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

使用Retrofit下载至Downloads子文件夹后无法通过ACTION_GET_CONTENT访问

Retrofit下载文件到Downloads子文件夹无法通过ACTION_GET_CONTENT/ACTION_OPEN_DOCUMENT访问的解决方法

问题现象

使用Retrofit下载文件至Downloads目录的自定义子文件夹(如Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)/MyApp):

  • 系统原生文件管理器可正常查看文件
  • 通过ACTION_GET_CONTENT或ACTION_OPEN_DOCUMENT Intent唤起的文件选择器无法找到该文件
  • 手动在文件管理器中重命名/复制该文件后,文件选择器可正常识别
  • 直接保存到Downloads根目录或使用DownloadManager下载时无此问题

问题原因

Android的媒体库依赖媒体扫描器索引外部存储文件。DownloadManager会自动触发媒体扫描通知系统文件变化;而手动写入文件到自定义子文件夹时,系统不会主动扫描该路径,导致基于媒体库的文件选择器无法发现新增文件。

解决方法

在文件写入完成后,调用MediaScannerConnection手动触发媒体扫描,通知系统更新文件索引。

修改后的下载函数

suspend fun download(url: String, targetPath: String, progressRetrofit: RetrofitProgress, context: Context) = flow {
    try {
        val response = apiService.download(url).awaitResponse()
        val body = response.body()
        if (response.isSuccessful && body != null) {
            try {
                val file = File(targetPath)
                // 确保父文件夹存在
                file.parentFile?.let { parentDir ->
                    if (!parentDir.exists()) {
                        parentDir.mkdirs()
                    }
                }
                body.byteStream().use { inputStream ->
                    FileOutputStream(file).use { outputStream ->
                        val data = ByteArray(1024)
                        var read: Int
                        var currentDownloadSize = 0L
                        val fileSize = body.contentLength()
                        while (inputStream.read(data).also { read = it } != -1) {
                            outputStream.write(data, 0, read)
                            currentDownloadSize += read
                            withContext(Dispatchers.Main) {
                                progressRetrofit.onProgressUpdate(
                                    (currentDownloadSize * 100 / fileSize).toInt(),
                                    fileSize,
                                    currentDownloadSize
                                )
                            }
                        }
                        withContext(Dispatchers.Main) {
                            progressRetrofit.onProgressUpdate(
                                (currentDownloadSize * 100 / fileSize).toInt(),
                                fileSize,
                                currentDownloadSize
                            )
                        }
                        outputStream.flush()
                        outputStream.close()
                    }
                }
                // 触发媒体扫描,通知系统文件新增
                MediaScannerConnection.scanFile(
                    context,
                    arrayOf(targetPath),
                    null // 若已知文件MIME类型可传入,如"application/pdf"
                ) { _, _ ->
                    // 扫描完成回调,按需处理
                }
                emit(NetworkResult.Success(true))
            } catch (e: Exception) {
                emit(NetworkResult.Failure(e.message.toString()))
                errorMessage(e.message.toString(), true)
            }
        } else {
            emit(NetworkResult.Failure(response.message()))
            errorMessage(response.errorBody().toString(), true)
        }
    } catch (e: Exception) {
        emit(NetworkResult.Failure(e.message.toString()))
        errorMessage(e.message.toString(), true)
    }
}

注意事项

  1. 确保已适配Android存储权限:Android 10及以上可使用Scoped Storage,无需申请WRITE_EXTERNAL_STORAGE;低版本需提前申请该权限
  2. 若明确文件MIME类型,在scanFile第三个参数传入对应值,可提升扫描效率
  3. 建议传入Application Context调用MediaScannerConnection,避免内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 17:50:37