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

iOS批量上传PHAsset时For循环漏处理资源且重复问题

问题核心原因

  • 存储路径重复:uniqueImageID和storageRef的定义放在了for循环外部,所有资源上传共用同一个存储路径,后上传的资源会直接覆盖前一个的存储文件,最终所有上传请求实际都上传到同一个路径,得到的下载URL完全相同,所以数据库里都是同一张图片的重复记录。
  • 提前关闭页面中断请求:你在单个资源上传完成的回调里直接调用了self.dismiss(animated: true, completion: nil),上传是异步操作,for循环只是触发了所有资源的上传请求,第一个请求完成就关闭了当前页面,后续未完成的上传请求会直接被中断,所以出现选5个只上传成功4个的情况。
  • 你怀疑的两行代码不是核心问题,但写法存在安全隐患:你先对url做了强制解包再执行guard校验,如果url为nil会直接触发崩溃,guard校验完全失去了意义。

修复方案

1. 调整变量作用域,保证每个资源对应独立存储路径

把uniqueImageID和storageRef的生成逻辑移到for循环内部,每次循环生成独立的唯一标识和存储路径。

2. 用DispatchGroup追踪所有上传任务的完成状态

只有所有上传任务全部执行完成后,再执行关闭页面的操作。

3. 优化可选值校验逻辑,移除不必要的强制解包

修复后的上传函数参考代码:

func uploadImageAsset(assets: [PHAsset], projectRef: DocumentReference) {
    let userID = Auth.auth().currentUser?.uid
    let db = Firestore.firestore()
    let manager = PHImageManager.default()
    let option = PHImageRequestOptions()
    option.isSynchronous = false
    option.isNetworkAccessAllowed = true
    option.resizeMode = .exact
    option.version = .original
    option.deliveryMode = .highQualityFormat
    
    let uploadGroup = DispatchGroup() // 用于追踪所有上传任务
    
    for asset in assets {
        uploadGroup.enter() // 每个上传任务开始前进入group
        
        let imageSize = CGSize(width: asset.pixelWidth, height: asset.pixelHeight)
        // 每个资源生成独立的存储路径
        let uniqueImageID = NSUUID().uuidString
        let storageRef = Storage.storage().reference().child("project-images").child("\(uniqueImageID).jpeg")
        
        manager.requestImage(for: asset, targetSize: imageSize, contentMode: .aspectFill, options: option) { (image, info) in
            guard let uploadData = image?.jpegData(compressionQuality: 0.6) else {
                uploadGroup.leave()
                return
            }
            storageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
                if error != nil {
                    uploadGroup.leave()
                    return
                } else {
                    storageRef.getMetadata(completion: { (metadata, err) in
                        if let error = err {
                            print(error)
                            uploadGroup.leave()
                            return
                        } else {
                            storageRef.downloadURL(completion: { (url, err) in
                                if let error = err {
                                    print(error)
                                    uploadGroup.leave()
                                    return
                                }
                                guard let urlStr = url?.absoluteString else { 
                                    uploadGroup.leave()
                                    return 
                                }
                                self.imageAssetURLs.append(urlStr)
                                
                                projectRef.updateData(["images": FieldValue.arrayUnion([urlStr])], completion: { (err) in
                                    if err != nil {
                                        print(err)
                                    }
                                    uploadGroup.leave() // 单个任务全部完成后离开group
                                })
                            })
                        }
                    })
                }
            })
        }
    }
    
    // 所有上传任务完成后执行关闭操作
    uploadGroup.notify(queue: .main) {
        self.dismiss(animated: true, completion: nil)
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 03:15:00