如何等待Firebase Storage完成图片上传后再创建帖子?
问题描述
我要创建一条帖子,但必须等图片上传到Firebase Storage后才能完成创建。目前的代码会创建imageURL为空的帖子,请问有没有办法在图片上传完成后从Firebase Storage获取返回值?
初始代码
private func createFeedPost(feedPost: FeedPost) { Task { await persistImageToStorage() } FirebaseManager.shared.firestore.collection("feedposts").document(feedPost.id) .setData(["commentsCount": feedPost.commentsCount, "description": feedPost.description, "feedImageUrl": feedPost.feedImageUrl, "id": feedPost.id, "likesCount": feedPost.likesCount, "locked": feedPost.locked, "price": feedPost.price, "timestamp": feedPost.timestamp]) }
private func persistImageToStorage() async { let ref = FirebaseManager.shared.storage.reference(withPath: imageId) guard let imageData = self.image?.jpegData(compressionQuality: 0.5) else { return } ref.putData(imageData, metadata: nil) { metadata, err in if let err = err { print(err) return } ref.downloadURL { url, err in if let err = err { print(err) return } guard let url = url else { return } self.imageUrl = url.absoluteString // 在这里设置imageURL } } }
编辑补充:尝试用completion但仍无法设置imageURL
private func persistImageToStorage(complete:()->()) { let ref = FirebaseManager.shared.storage.reference(withPath: imageId) guard let imageData = self.image?.jpegData(compressionQuality: 0.5) else { return } ref.putData(imageData, metadata: nil) { metadata, err in if let err = err { print(err) return } ref.downloadURL { url, err in if let err = err { print(err) return } guard let url = url else { return } self.imageUrl = url.absoluteString } } complete() } private func createFeedPost(feedPost: FeedPost) { persistImageToStorage(complete: { () -> () in FirebaseManager.shared.firestore.collection("feedposts").document(feedPost.id).setData(["commentsCount": feedPost.commentsCount, "description": feedPost.description, "feedImageUrl": feedPost.feedImageUrl, "id": feedPost.id, "likesCount": feedPost.likesCount, "locked": feedPost.locked, "price": feedPost.price, "timestamp": feedPost.timestamp]) }) }
解决方案
方式一:正确使用Async/Await(推荐)
你的persistImageToStorage标记为async但内部还是用了回调,没有真正实现异步等待,导致createFeedPost里的Firestore写入直接执行了。需要把Firebase的回调转为异步函数:
private func persistImageToStorage() async throws -> String? { let ref = FirebaseManager.shared.storage.reference(withPath: imageId) guard let imageData = self.image?.jpegData(compressionQuality: 0.5) else { return nil } // 用withCheckedThrowingContinuation把回调转为async/await return try await withCheckedThrowingContinuation { continuation in ref.putData(imageData, metadata: nil) { metadata, err in if let err = err { continuation.resume(throwing: err) return } ref.downloadURL { url, err in if let err = err { continuation.resume(throwing: err) return } continuation.resume(returning: url?.absoluteString) } } } } private func createFeedPost(feedPost: FeedPost) { Task { do { // 等待图片上传完成并获取URL if let imageUrl = try await persistImageToStorage() { // 更新feedPost的imageUrl var updatedPost = feedPost updatedPost.feedImageUrl = imageUrl // 写入Firestore try await FirebaseManager.shared.firestore.collection("feedposts") .document(updatedPost.id) .setData([ "commentsCount": updatedPost.commentsCount, "description": updatedPost.description, "feedImageUrl": updatedPost.feedImageUrl, "id": updatedPost.id, "likesCount": updatedPost.likesCount, "locked": updatedPost.locked, "price": updatedPost.price, "timestamp": updatedPost.timestamp ]) } } catch { print("上传图片或创建帖子失败:\(error)") } } }
方式二:正确使用Completion Handler
你之前的completion问题在于complete()调用时机不对——放在了putData外面,没等上传和获取URL的回调完成,导致Firestore写入时imageUrl还没设置。需要把complete()移到获取URL后的代码块里:
private func persistImageToStorage(complete: @escaping (String?) -> Void) { let ref = FirebaseManager.shared.storage.reference(withPath: imageId) guard let imageData = self.image?.jpegData(compressionQuality: 0.5) else { complete(nil) return } ref.putData(imageData, metadata: nil) { metadata, err in if let err = err { print(err) complete(nil) return } ref.downloadURL { url, err in if let err = err { print(err) complete(nil) return } let imageUrl = url?.absoluteString self.imageUrl = imageUrl complete(imageUrl) // 在这里调用completion,确保URL已获取 } } } private func createFeedPost(feedPost: FeedPost) { persistImageToStorage { [weak self] imageUrl in guard let self = self, let imageUrl = imageUrl else { print("图片上传失败或URL为空") return } var updatedPost = feedPost updatedPost.feedImageUrl = imageUrl FirebaseManager.shared.firestore.collection("feedposts") .document(updatedPost.id) .setData([ "commentsCount": updatedPost.commentsCount, "description": updatedPost.description, "feedImageUrl": updatedPost.feedImageUrl, "id": updatedPost.id, "likesCount": updatedPost.likesCount, "locked": updatedPost.locked, "price": updatedPost.price, "timestamp": updatedPost.timestamp ]) { err in if let err = err { print("创建帖子失败:\(err)") } } } }
关键问题说明
- 初始代码中,
Task { await persistImageToStorage() }是异步执行的,不会阻塞后面的Firestore写入,所以写入时imageUrl还没更新。 - Completion方式的错误是
complete()提前调用,没有等待downloadURL的回调完成,导致Firestore写入时URL未就绪。 - 推荐用Async/Await方式,代码更简洁易读,避免回调地狱。
内容的提问来源于stack exchange,提问作者AdamLeet
相关产品推荐
相关产品推荐

