Firebase注册时头像上传至Firestore失败问题求助
Firebase注册功能头像上传失败排查
在Firebase实现用户注册功能时,RegisterViewController页面包含用户名、邮箱、密码及头像字段,Firestore数据结构已配置完成。但选择头像、填写信息点击注册按钮后,头像无法上传至Firebase Storage,控制台报错:Firebase Storage 업로드 실패: Object profile_images/rRMJBmF4NlYnlOmVxawxGZqkwzu2.jpg does not exist
代码功能说明
用户注册(registerUser)
- 通过Firebase Auth使用邮箱和密码创建用户账号
- 创建成功后获取用户
userUID
头像上传与Firestore保存(uploadProfileAndSaveUserToFirestore)
- 将头像上传至Firebase Storage
- 上传完成后获取下载URL
- 将
email、username、profileImageURL保存至Firestore的users集合对应userUID的文档中
Firestore用户信息保存(saveUserToFirestore)
- 在Firestore中存储
username、email、profileImageURL字段及createdAt时间戳
相关代码
RegisterUserRequest结构
import Foundation import UIKit struct RegisterUserRequest { let username: String let email: String let password: String var userImage: UIImage }
AuthService类相关方法
func registerUser(request: RegisterUserRequest, completion: @escaping (Bool, Error?) -> Void) { // 1. Firebase Auth로 사용자 생성 Auth.auth().createUser(withEmail: request.email, password: request.password) { authResult, error in if let error = error { print("Firebase Auth 사용자 생성 실패: \(error.localizedDescription)") completion(false, error) return } guard let userUID = authResult?.user.uid else { let uidError = NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "사용자 UID를 가져올 수 없습니다."]) completion(false, uidError) return } // 2. 이미지 업로드와 Firestore 데이터 저장 통합 self.uploadProfileAndSaveUserToFirestore(userUID: userUID, request: request) { success, error in if success { print("회원가입 성공") completion(true, nil) } else { print("회원가입 실패: \(error?.localizedDescription ?? "알 수 없는 에러")") completion(false, error) } } } } private func uploadProfileAndSaveUserToFirestore(userUID: String, request: RegisterUserRequest, completion: @escaping (Bool, Error?) -> Void) { // 1. 이미지를 Firebase Storage에 업로드 guard let imageData = request.userImage.jpegData(compressionQuality: 0.8) else { completion(false, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "이미지 데이터를 변환할 수 없습니다."])) return } let storageRef = Storage.storage().reference().child("profile_images/\(userUID).jpg") storageRef.putData(imageData, metadata: nil) { metadata, error in if let error = error { print("Firebase Storage 업로드 실패: \(error.localizedDescription)") completion(false, error) return } // 2. 다운로드 URL 가져오기 storageRef.downloadURL { url, error in if let error = error { print("Firebase Storage 다운로드 URL 가져오기 실패: \(error.localizedDescription)") completion(false, error) return } guard let profileImageURL = url?.absoluteString else { completion(false, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "이미지 URL을 가져올 수 없습니다."])) return } // 3. Firestore에 사용자 정보 저장 self.saveUserToFirestore(userUID: userUID, username: request.username, email: request.email, profileImageURL: profileImageURL) { success, error in completion(success, error) } } } } private func saveUserToFirestore(userUID: String, username: String, email: String, profileImageURL: String, completion: @escaping (Bool, Error?) -> Void) { let db = Firestore.firestore() let userData: [String: Any] = [ "username": username, "email": email, "profileImageURL": profileImageURL, "createdAt": Timestamp() ] db.collection("users").document(userUID).setData(userData) { error in if let error = error { print("Firestore 저장 실패: \(error.localizedDescription)") completion(false, error) } else { print("Firestore 저장 성공") completion(true, nil) } } }
RegisterController的PHPickerViewControllerDelegate扩展
extension RegisterController: PHPickerViewControllerDelegate { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { // PHPicker 닫기 picker.dismiss(animated: true) // 첫 번째 선택된 아이템 가져오기 let itemProvider = results.first?.itemProvider if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) { // UIImage 불러오기 itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in if let image = image as? UIImage { // 불러온 이미지 타입 확인 DispatchQueue.main.async { // CustomImageView에 이미지 설정 self.profileImage = image self.userPofileView.setImageType(.user(image)) } } else { print("이미지가 올바르지 않습니다.") } } } else { print("이미지를 불러올 수 없습니다.") } } }
RegisterController的注册按钮点击事件
// MARK: - Selectors @objc private func didTapSignUp() { let username = self.usernameField.text ?? "" let email = self.emailField.text ?? "" let password = self.passwordField.text ?? "" let userImage = profileImage ?? UIImage() let registerUserRequest = RegisterUserRequest(username: username, email: email, password: password, userImage: userImage) print("userImage: \(userImage)") // Check Username if !Validator.isValidUsername(for: registerUserRequest.username) { AlertManager.showInvalidUsernameAlert(on: self) return } // Check Email if !Validator.isValidEmail(for: registerUserRequest.email) { AlertManager.showInvalidEmailAlert(on: self) return } // Check Password if !Validator.isValidPassword(for: registerUserRequest.password) { AlertManager.showInvalidPasswordAlert(on: self) return } AutheService.shared.registerUser(request: registerUserRequest) { wasRegistered, error in if let error = error { AlertManager.showSignInErrorAlert(on: self, with: error) return } if wasRegistered { if let sceneDelegate = self.view.window?.windowScene?.delegate as? SceneDelegate { sceneDelegate.checkAuthentication() } } else { AlertManager.showSignInErrorAlert(on: self, with: NSError(domain: "", code: 500, userInfo: [NSLocalizedDescriptionKey: "회원가입 실패"])) } } }
问题排查与解决方案
1. 空UIImage导致上传失败
注册时若用户未选择头像,代码会使用UIImage()空实例,该空图片无法转换为有效JPEG数据,导致Storage中无实际文件,后续获取URL时提示对象不存在。
- 修复:强制用户选择头像,或提供默认头像:
guard let userImage = profileImage else { AlertManager.showErrorAlert(on: self, with: "프로필 이미지를 선택해주세요.") return }
2. Firebase Storage权限配置问题
默认Storage规则可能拒绝上传请求,导致文件未写入,后续获取URL时提示对象不存在。
- 测试阶段临时调整规则为公开读写(上线需修改为安全规则):
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if true; } } } - 上线时配置为仅认证用户可上传自己的头像:
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /profile_images/{userId}.jpg { allow read: if request.auth != null; allow write: if request.auth != null && request.auth.uid == userId; } } }
3. 图片转换失败
部分图片格式(如带透明通道的PNG)转换为JPEG时可能失败,或图片本身损坏导致无法生成有效数据。
- 修复:同时支持PNG和JPEG转换:
guard let imageData = request.userImage.pngData() ?? request.userImage.jpegData(compressionQuality: 0.8) else { completion(false, NSError(domain: "", code: -1, userInfo: [NSLocalizedDescriptionKey: "이미지 데이터를 변환할 수 없습니다."])) return }
4. PHPicker图片加载异常
PHPicker异步加载图片时可能失败,但原代码未处理该错误,导致profileImage仍为空。
- 修复:添加加载错误处理:
itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in if let error = error { print("이미지 로드 실패: \(error.localizedDescription)") DispatchQueue.main.async { AlertManager.showErrorAlert(on: self, with: "이미지를 로드할 수 없습니다.") } return } if let image = image as? UIImage { DispatchQueue.main.async { self.profileImage = image self.userPofileView.setImageType(.user(image)) } } else { print("이미지가 올바르지 않습니다.") DispatchQueue.main.async { AlertManager.showErrorAlert(on: self, with: "유효한 이미지를 선택해주세요.") } } }
内容的提问来源于stack exchange,提问作者권정근
相关产品推荐
相关产品推荐

