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

Android(Java):通过Firebase Storage下载URL下载文件至指定文件夹

解决方案:直接下载Firebase Storage文件到指定文件夹

核心思路

替代跳转浏览器的方案,主要有三种实现方式,分别适配不同需求场景,下面逐一说明:


方案一:用系统DownloadManager实现(推荐,省心稳定)

系统原生下载组件,自动处理后台下载、通知提示、权限兼容,适合简单下载场景。

步骤1:配置权限

在AndroidManifest.xml中添加必要权限:

<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 旧版本存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Android 10+ 读取权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<!-- Android 10+ 兼容旧存储模型(可选,用于公共目录自定义文件夹) -->
<application
    ...
    android:requestLegacyExternalStorage="true">

步骤2:点击事件中调用下载逻辑

// RecyclerView的CardView点击事件
cardView.setOnClickListener {
    val fileUrl = "你的Firebase下载URL"
    val fileName = "08-11-2022_15:43:02_dummy.pdf" // 从列表数据中获取文件名
    downloadWithSystemManager(it.context, fileUrl, fileName)
}

// 系统DownloadManager下载方法
private fun downloadWithSystemManager(context: Context, url: String, fileName: String) {
    val request = DownloadManager.Request(Uri.parse(url))
        .setTitle(fileName)
        .setDescription("文件下载中")
        .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
        // 保存到Download目录下的自定义文件夹(比如SmartEdubox)
        .setDestinationInExternalPublicDir("SmartEdubox", fileName)
        .setAllowedOverMetered(true)
        .setAllowedOverRoaming(true)

    val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
    downloadManager.enqueue(request)
}

方案二:用Firebase Storage SDK直接下载(避免URL过期)

如果你的列表数据中存储的是StorageReference而非临时下载URL,用SDK直接下载更可靠,还能监听进度。

代码示例

// 从Firebase获取文件引用(替换为你的文件路径)
val storageRef = FirebaseStorage.getInstance()
    .getReference("uploaded_files/pdf_files/08-11-2022_15:43:02_dummy.pdf")

// 指定保存路径(外部存储Download目录下的自定义文件夹)
val saveDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "SmartEdubox")
if (!saveDir.exists()) saveDir.mkdirs()
val targetFile = File(saveDir, "08-11-2022_15:43:02_dummy.pdf")

// 开始下载
storageRef.getFile(targetFile)
    .addOnSuccessListener {
        Toast.makeText(context, "文件已保存至${targetFile.path}", Toast.LENGTH_SHORT).show()
    }
    .addOnFailureListener { e ->
        Toast.makeText(context, "下载失败:${e.message}", Toast.LENGTH_SHORT).show()
    }
    .addOnProgressListener { snapshot ->
        // 实时监听下载进度
        val progress = (100.0 * snapshot.bytesTransferred / snapshot.totalByteCount).toInt()
        Log.d("Download", "进度:$progress%")
    }

方案三:OkHttp手动下载(高度自定义)

适合需要自定义下载逻辑、断点续传等场景,需自己处理线程和文件流。

代码示例

private fun downloadWithOkHttp(context: Context, url: String, savePath: String) {
    val client = OkHttpClient()
    val request = Request.Builder().url(url).build()

    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            // 切换到主线程提示错误
            Handler(Looper.getMainLooper()).post {
                Toast.makeText(context, "下载失败:${e.message}", Toast.LENGTH_SHORT).show()
            }
        }

        override fun onResponse(call: Call, response: Response) {
            if (!response.isSuccessful) {
                Handler(Looper.getMainLooper()).post {
                    Toast.makeText(context, "请求失败:${response.code}", Toast.LENGTH_SHORT).show()
                }
                return
            }

            // 写入文件
            response.body?.byteStream()?.use { input ->
                FileOutputStream(savePath).use { output ->
                    input.copyTo(output)
                }
            }

            // 下载完成提示
            Handler(Looper.getMainLooper()).post {
                Toast.makeText(context, "文件已保存至$savePath", Toast.LENGTH_SHORT).show()
            }
        }
    })
}

// 调用方法
val saveDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "SmartEdubox")
if (!saveDir.exists()) saveDir.mkdirs()
val savePath = "${saveDir.absolutePath}/08-11-2022_15:43:02_dummy.pdf"
downloadWithOkHttp(context, fileUrl, savePath)

Android 10+ 存储权限注意事项

如果不想申请MANAGE_EXTERNAL_STORAGE权限,可改用MediaStore写入公共目录,无需传统存储权限:

private fun saveToMediaStore(context: Context, inputStream: InputStream, fileName: String) {
    val contentValues = ContentValues().apply {
        put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
        put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf") // 根据文件类型修改
        put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/SmartEdubox")
    }

    val uri = context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
    uri?.let {
        context.contentResolver.openOutputStream(it)?.use { output ->
            inputStream.copyTo(output)
            Toast.makeText(context, "文件下载完成", Toast.LENGTH_SHORT).show()
        }
    }
}

内容的提问来源于stack exchange,提问作者Dennis Manicani Jr.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 22:05:29