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

iOS视频缩略图生成成功但消息占位图未更新问题求助

视频缩略图生成成功但占位图未更新的排查方案

问题背景

控制台已打印Thumbnail generated successfully,说明缩略图生成逻辑正常,但视频消息的占位图未替换为生成的缩略图。视频通过StorageManager上传至Firebase Storage,相关代码如下:

生成缩略图的函数

func generateThumbnail(for videoUrl: URL, completion: @escaping (UIImage?) -> Void) {
    DispatchQueue.global().async {
        let asset = AVURLAsset(url: videoUrl)
        let imageGenerator = AVAssetImageGenerator(asset: asset)
        imageGenerator.appliesPreferredTrackTransform = true
        
        let time = CMTime(seconds: 1, preferredTimescale: 60) // Get thumbnail at 1 second
        do {
            let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil)
            let thumbnail = UIImage(cgImage: cgImage)
            DispatchQueue.main.async {
                completion(thumbnail)
                print("Thumbnail generated successfully")
            }
        } catch {
            print("Error generating thumbnail: \(error)")
            DispatchQueue.main.async {
                completion(nil)
            }
        }
    }
}

调用函数及视频上传至Firebase Storage的代码

if let videoUrl = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
    let fileName = "photo_message_" + messageId.replacingOccurrences(of: " ", with: "-") + ".mov"
    
    generateThumbnail(for: videoUrl) { [weak self] thumbnailImage in
        if let thumbnailImage = thumbnailImage {
    
            //Upload Video
            StorageManager.shared.uploadMessageVideo(with: videoUrl, fileName: fileName, completion: { [weak self] result in
                guard let strongSelf = self else {
                    return
                }
                
                switch result {
                case .success(let urlString):
                    //Ready to send message
                    print("Uploaded Message Video: \(urlString)")
                    
                    guard let url = URL(string: urlString) else {
                        return
                    }
                    
                    let media = Media(url: url,
                                      image: nil,
                                      placeholderImage: thumbnailImage,
                                      size: .zero)
                    
                    let message = Message(sender: selfSender,
                                          messageId: messageId,
                                          sentDate: Date(),
                                          kind: .video(media))
                    
                    DatabaseManager.shared.sendMessage(to: conversationId, otherUserEmail: strongSelf.otherUserEmail, name: name, newMessage: message, completion: { success in
                        if success {
                            print ("Sent Video Message")
                        } else {
                            print("Failed to send photo messsage")
                        }
                    })
                case .failure(let error):
                    print("message video upload error: \(error)")
                }
            })
        }
    }
}
picker.dismiss(animated: true, completion: nil)

排查方向

1. 缺少UI刷新逻辑

控制台打印Sent Video Message仅代表消息成功存入数据库,但没有触发界面刷新。如果消息列表用UITableView/CollectionView展示,需要在sendMessage的成功回调里调用刷新方法:

DatabaseManager.shared.sendMessage(...) { success in
    if success {
        print ("Sent Video Message")
        // 主线程刷新UI
        DispatchQueue.main.async {
            self?.messageTableView.reloadData()
            // 或者定位到新增的消息行刷新,提升性能
            // self?.messageTableView.insertRows(at: [IndexPath(row: self?.messages.count ?? 0, section: 0)], with: .automatic)
        }
    } else {
        print("Failed to send photo messsage")
    }
}

2. Media/Message模型的UI绑定错误

  • 检查UI渲染代码是否正确读取Media的placeholderImage属性,而非错误使用image(当前代码中image被设为nil)。
  • 确认Message.kind.video(media)类型在解析时,正确将占位图传递给对应的UIImageView。

3. 缩略图未上传至Firebase(持久化问题)

当前代码仅将本地生成的UIImage存入Media对象,但UIImage无法直接序列化到Firebase数据库。如果消息列表是从数据库拉取(而非仅本地缓存),后续加载时会丢失占位图:

  1. 需要先将缩略图上传至Firebase Storage,拿到缩略图URL;
  2. 将缩略图URL存入Media对象(而非直接存UIImage);
  3. UI层通过URL加载缩略图。

示例修改逻辑:

generateThumbnail(for: videoUrl) { [weak self] thumbnailImage in
    if let thumbnailImage = thumbnailImage {
        // 先上传缩略图
        let thumbnailFileName = "thumbnail_" + messageId.replacingOccurrences(of: " ", with: "-") + ".png"
        StorageManager.shared.uploadMessageImage(with: thumbnailImage, fileName: thumbnailFileName) { thumbnailResult in
            switch thumbnailResult {
            case .success(let thumbnailUrlString):
                // 再上传视频
                StorageManager.shared.uploadMessageVideo(with: videoUrl, fileName: fileName) { videoResult in
                    // 后续创建Media时传入缩略图URL,而非UIImage
                    let media = Media(url: videoUrl,
                                      imageUrl: thumbnailUrlString,
                                      placeholderImage: thumbnailImage, // 本地临时显示用
                                      size: .zero)
                    // ... 后续创建消息并发送
                }
            case .failure(let error):
                print("thumbnail upload error: \(error)")
            }
        }
    }
}

4. 图片显示的尺寸/适配问题

  • 生成缩略图后打印尺寸,确认图片有效:
    DispatchQueue.main.async {
        print("Thumbnail size: \(thumbnailImage.size)")
        completion(thumbnail)
        print("Thumbnail generated successfully")
    }
    
  • 检查UIImageView的contentMode设置,建议设为.scaleAspectFill或.scaleAspectFit,避免图片显示异常。

5. 上下文对象被提前释放

代码中使用[weak self],需确认当前ViewController在整个异步流程(缩略图生成→视频上传→消息发送)中未被释放。可在关键位置打印self的内存地址,或添加断点验证。


内容的提问来源于stack exchange,提问作者Tomas De Andrade

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 03:47:11