使用Kotlin导出Firebase数据至XLSX无反应且找不到文件求助
问题描述
使用Kotlin结合Apache POI库将Firebase数据库中的数据导出为XLSX文件,程序运行无报错,但无法找到导出的文件。
代码示例
val db = FirebaseDatabase.getInstance().getReference("aktivitas") btndownload.setOnClickListener{ val query_kegiatan = db.orderByChild("aktivitas") query_kegiatan.addValueEventListener(object : ValueEventListener{ override fun onDataChange(snapshot: DataSnapshot) { val workbook = XSSFWorkbook() val sheet = workbook.createSheet("Aktivitas") var rownum = 0 for(item in snapshot.children){ val row = sheet.createRow(rownum++) row.createCell(0).setCellValue(item.child("bagian").value as String) row.createCell(1).setCellValue(item.child("barang").value as String) row.createCell(2).setCellValue(item.child("jumlah").value as String) row.createCell(3).setCellValue(item.child("keterangan").value as String) val save = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),"aktivitas.xlsx") val fos = FileOutputStream(save) workbook.write(fos) } } override fun onCancelled(error: DatabaseError) { TODO("Not yet implemented") } }) }
预期目标:成功将Firebase中的数据导出至XLSX文件。
解决方案
1. 修正文件写入逻辑(核心问题)
你把workbook.write(fos)放在了遍历数据的for循环内部,会导致每次处理一条数据就重复写入文件,不仅效率低下,还可能因文件流未正确关闭导致文件损坏或无法生成。需将文件写入代码移到循环外部,并自动管理流资源:
override fun onDataChange(snapshot: DataSnapshot) { val workbook = XSSFWorkbook() val sheet = workbook.createSheet("Aktivitas") var rownum = 0 for(item in snapshot.children){ val row = sheet.createRow(rownum++) row.createCell(0).setCellValue(item.child("bagian").value as String) row.createCell(1).setCellValue(item.child("barang").value as String) row.createCell(2).setCellValue(item.child("jumlah").value as String) row.createCell(3).setCellValue(item.child("keterangan").value as String) } // 循环结束后统一写入文件 val save = File(context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),"aktivitas.xlsx") FileOutputStream(save).use { fos -> workbook.write(fos) } workbook.close() }
use函数会自动关闭文件流,避免资源泄漏,最后关闭workbook释放内存。
2. 明确文件存储路径
当前使用的context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)是应用私有存储目录,路径为/storage/emulated/0/Android/data/[你的应用包名]/files/Download/aktivitas.xlsx,系统默认“下载”文件夹不会显示这里的文件。
- 若想导出到公共下载目录,需先在
AndroidManifest.xml添加权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32"/> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33"/>
- 动态申请权限(Android 6.0+)后,修改路径:
val publicDownloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) val save = File(publicDownloadDir, "aktivitas.xlsx")
注意:Android 13+对公共存储权限限制更严格,优先推荐使用应用私有目录,或改用MediaStore API写入公共目录。
3. 替换监听事件类型
addValueEventListener会持续监听数据变化,每次数据更新都会触发导出,应改用addListenerForSingleValueEvent,仅执行一次数据读取和导出:
query_kegiatan.addListenerForSingleValueEvent(object : ValueEventListener{ // 其余逻辑不变 })
4. 处理空值与异常
避免强制类型转换as String导致的潜在崩溃,同时捕获IO异常并给出提示:
// 安全获取字符串值,空值时用空字符串替代 row.createCell(0).setCellValue(item.child("bagian").value?.toString() ?: "") row.createCell(1).setCellValue(item.child("barang").value?.toString() ?: "") row.createCell(2).setCellValue(item.child("jumlah").value?.toString() ?: "") row.createCell(3).setCellValue(item.child("keterangan").value?.toString() ?: "") // 捕获IO异常并提示结果 try { FileOutputStream(save).use { fos -> workbook.write(fos) } workbook.close() Toast.makeText(context, "导出成功", Toast.LENGTH_SHORT).show() } catch (e: IOException) { e.printStackTrace() Toast.makeText(context, "导出失败: ${e.message}", Toast.LENGTH_SHORT).show() }
内容的提问来源于stack exchange,提问作者daffa Firmansyah
相关产品推荐
相关产品推荐

