如何实现Firebase Firestore与Storage同步删除 异常时均不删除
问题根因
你当前的串行删除逻辑完全没有一致性保护,两个操作分别是独立的网络请求:只要图片删除成功后、笔记文档删除前出现任何异常(网络闪断、权限校验失败、服务端临时错误),就会出现「图片已经被删但笔记残留」的脏数据,根本达不到两个资源要么同时删除成功、要么全部保留的要求。
实现思路
Firebase Storage的文件操作和Firestore的文档操作不在同一个原生事务支持范围内,没法直接用单事务包裹两个操作实现原子性,工业界通用的解法是用「软删除标记+失败回滚+定时兜底重试」的方案,完全规避中间态问题:
- 第一步:删除操作触发时,不要直接硬删任何真实资源,先通过单文档原子更新给目标笔记打上
pendingDelete待删除标记,同时记录关联的所有图片地址、操作触发时间。这步要么成功要么直接失败,不会产生中间状态。 - 第二步:待标记写入成功后,再依次执行关联图片删除、笔记硬删除操作。
- 两个操作全部执行成功,删除流程正常结束。
- 任意环节抛出错误,立刻执行回滚:清除笔记上的待删除标记,把笔记恢复为正常可见状态;如果已经有部分图片被误删,就从备份存储回拷对应文件,保证笔记内容完整可用。
- 第三步:配置定时触发的云函数,定期扫描带
pendingDelete标记且超过超时阈值(建议设为5-10分钟)的笔记,自动重试剩余的删除步骤,避免临时故障导致删除流程卡死、资源残留。
参考实现代码
Future<void> deleteNote(QueryDocumentSnapshot noteSnapshot) async { final targetNoteId = noteSnapshot.id; final linkedImageUrl = noteSnapshot['imageURL'] as String; final imageRef = FirebaseStorage.instance.refFromURL(linkedImageUrl); final noteDocRef = notesReference.doc(targetNoteId); try { // 原子写入待删除标记,这步失败直接终止流程,不会改动任何资源 await noteDocRef.update({ 'pendingDelete': true, 'deleteTriggerTime': FieldValue.serverTimestamp(), }); // 删除关联存储图片 await imageRef.delete(); // 硬删除笔记文档 await noteDocRef.delete(); } catch (err) { // 任意步骤出错立刻回滚,清除待删除标记恢复笔记 // 若业务要求高一致性,可在此处添加已删图片的恢复逻辑 await noteDocRef.update({ 'pendingDelete': FieldValue.delete(), 'deleteTriggerTime': FieldValue.delete(), }); // 可按需将错误抛到上层,给用户弹出删除失败提示 rethrow; } }
落地注意事项
- 不要调换删除顺序先删笔记再删图片,否则会出现笔记已经消失但存储图片残留的问题,长期累积会浪费大量存储额度。
- 正常的笔记列表查询逻辑要加上
where('pendingDelete', isEqualTo: false)过滤条件,避免待删除的笔记展示给用户。 - 如果单条笔记关联多张图片,软删除阶段要把所有图片的URL都记录到笔记字段中,只有全部图片删除成功才执行笔记硬删,只要任意一张图片删除失败就全量回滚。
内容的提问来源于stack exchange,提问作者Esraa Hamada
相关产品推荐
相关产品推荐

