You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何等待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)")
                }
            }
    }
}

关键问题说明

  1. 初始代码中,Task { await persistImageToStorage() }是异步执行的,不会阻塞后面的Firestore写入,所以写入时imageUrl还没更新。
  2. Completion方式的错误是complete()提前调用,没有等待downloadURL的回调完成,导致Firestore写入时URL未就绪。
  3. 推荐用Async/Await方式,代码更简洁易读,避免回调地狱。

内容的提问来源于stack exchange,提问作者AdamLeet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 08:21:57