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.
相关产品推荐
相关产品推荐

