我正在用 Kotlin 编写我的第一个应用,并且正在使用 Firestore 和 Firebase Storage。在删除文档的过程中,我想删除文档引用的 Storage 中的所有文件(因为在我的情况下它是对它们的唯一引用)。如果存储删除失败,我想中止文档删除,以避免存储中的孤立文件。我还想在“一个任务”中做所有事情,以允许正确显示进度条。我的简化代码如下所示:
fun deleteItem(id: String): Task<Void>? {
val deleteTask = deleteTaleMedia(id)
continueWithTaskOrInNew(deleteTask) { task ->
if (task?.isSuccessful != false) { ... }
}
}
fun deleteItemMedia(id: String): Task<Void>? =
getItem(id)?.continueWithTask { task ->
if (task.isSuccessful)
task.result?.toObject(ItemModel::class.java)?.let { deleteFiles(it.media) }
else ???
}
fun deleteFiles(filesList: List<String>): Task<Void>? {
var deleteTask: Task<Void>? = null
for (file in filesList) deleteTask = continueWithTaskOrInNew(deleteTask) { task ->
if (task?.isSuccessful != false) deleteFile(file)
else task
}
return task
}
fun deleteFile(fileName: String) = Firebase.storage.getReferenceFromUrl(fileName).delete()
fun getItem(id: String): Task<DocumentSnapshot>? {
val docRef = userDocRef?.collection(COLLECTION_PATH)?.document(id)
return docRef?.get()
?.addOnCompleteListener { ... }
}
fun <ResultT, TContinuationResult> continueWithTaskOrInNew(
task: Task<ResultT>?,
continuation: (Task<ResultT>?) -> Task<TContinuationResult>?
) = task?.continueWithTask { continuation.invoke(task) } ?: continuation.invoke(null)
data class ItemModel(
@DocumentId val id: String = "",
var title: String = "",
var media: List<String> = listOf()
)
我的问题出在 deleteItemMedia
函数中(末尾的“???”)。如果 get 任务失败,我想返回一个任务,告诉我的 deleteItem
函数中止删除 (task.isSuccessful == false
)。我无法返回获取任务本身(在代码中将“???”替换为“任务”),因为它的类型 (Task<DocumentSnapshot>
) 与删除任务的类型 (Task<Void>
) 不同。我无法返回 null
,因为在根本没有媒体的情况下返回 null
,这对我来说是有效的情况(应该删除文档)。有没有办法创建一个新的“失败任务”?
答案 0 :(得分:1)
在删除文档的过程中,我想删除文档引用的 Storage 中的所有文件(因为在我的情况下它是对它们的唯一引用)。
没有这样做的 API。您必须自己执行这两个删除操作。
<块引用>我还想在“一个任务”中做所有事情,以便正确显示进度条。
不幸的是,这不可能一次性完成。如果您考虑原子操作,这也是不可能的,因为没有一个 Firebase 服务支持这种跨产品事务操作。您需要做的是,获取文档,获取对存储中文件的引用,删除文档,并在删除操作完成后立即删除文件。您绝对可以通过尝试从客户端回滚数据来降低风险,但是您不能在“一个任务”中将它们原子化。但是,在某个时间点,会出现客户端无法回滚的异常。
<块引用>如果存储删除失败,我想中止文档删除,以避免我的存储中出现孤立文件。
为了避免这种情况,首先,尽量不要有不完整的数据。例如,当您阅读文档并获得相应的存储 URL 时,不要盲目地假设所有这些文件都确实存在。文件可能因多种原因不可用(之前被删除,由于某些原因服务不可用等)
另一种方法可能是使用Cloud Functions for Firebase,这样您就可以删除所需的文件,并使用onDelete函数从Storage中删除相应的文件。意思是,当文档删除失败时,存储中的文件不会被删除。如果删除文档操作成功,将触发云功能,并将图像从存储中删除。这种方法将大大减少文档删除操作和从 Storage 中删除文件之间出现故障的几率,但并不能 100% 消除这种几率。
除此之外,避免失败的最常见方法是使您的代码尽可能健壮以防止失败,并频繁地清理数据库。