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
相关产品推荐
相关产品推荐

