如何在继续任务中返回失败的任务结果

时间:2021-01-15 22:59:57

标签: firebase kotlin google-cloud-firestore firebase-storage google-tasks-api

我正在用 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,这对我来说是有效的情况(应该删除文档)。有没有办法创建一个新的“失败任务”?

1 个答案:

答案 0 :(得分:1)

<块引用>

在删除文档的过程中,我想删除文档引用的 Storage 中的所有文件(因为在我的情况下它是对它们的唯一引用)。

没有这样做的 API。您必须自己执行这两个删除操作。

<块引用>

我还想在“一个任务”中做所有事情,以便正确显示进度条。

不幸的是,这不可能一次性完成。如果您考虑原子操作,这也是不可能的,因为没有一个 Firebase 服务支持这种跨产品事务操作。您需要做的是,获取文档,获取对存储中文件的引用,删除文档,并在删除操作完成后立即删除文件。您绝对可以通过尝试从客户端回滚数据来降低风险,但是您不能在“一个任务”中将它们原子化。但是,在某个时间点,会出现客户端无法回滚的异常。

<块引用>

如果存储删除失败,我想中止文档删除,以避免我的存储中出现孤立文件。

为了避免这种情况,首先,尽量不要有不完整的数据。例如,当您阅读文档并获得相应的存储 URL 时,不要盲目地假设所有这些文件都确实存在。文件可能因多种原因不可用(之前被删除,由于某些原因服务不可用等)

另一种方法可能是使用Cloud Functions for Firebase,这样您就可以删除所需的文件,并使用onDelete函数从Storage中删除相应的文件。意思是,当文档删除失败时,存储中的文件不会被删除。如果删除文档操作成功,将触发云功能,并将图像从存储中删除。这种方法将大大减少文档删除操作和从 Storage 中删除文件之间出现故障的几率,但并不能 100% 消除这种几率。

除此之外,避免失败的最常见方法是使您的代码尽可能健壮以防止失败,并频繁地清理数据库