使用 Firebase 和 Swift (iOS) 上传图片

Posted

技术标签:

【中文标题】使用 Firebase 和 Swift (iOS) 上传图片【英文标题】:Uploading an Image with Firebase & Swift (iOS) 【发布时间】:2018-06-25 01:50:24 【问题描述】:

在开始之前,我想预先警告我的代码很可能不正确,因为我是 Swift 编码的初学者。

我正在为一个大学项目创建一个应用程序,这是我创建的第一个大型应用程序,而且我已经很久没有编写代码了。

我在使用 firebase 创建帐户时尝试上传图像时遇到问题,我之前的代码工作正常,但我正在使用工作正常的“childByAutoId()”写入数据库,但我意识到我需要写入数据库并通过用户 ID 保存它。在我将 'childByAutoId()' 更改为 'child(uid)' 这是我的用户 ID 前缀变量后,它停止上传图像,我不知道为什么。我试图回到它与 childByAutoId() 一起工作的时候,但现在这也不起作用。

我的代码:

import UIKit
import Firebase
import FirebaseStorage

class RegisterViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate 

@IBOutlet weak var profileImage: UIImageView!
@IBOutlet weak var usernameField: UITextField!
@IBOutlet weak var emailField: UITextField!
@IBOutlet weak var passwordField: UITextField!
@IBOutlet weak var dobField: UITextField!
@IBOutlet weak var selectImageButton: UIButton!

var imageFileName = ""

override func viewDidLoad() 
    super.viewDidLoad()
    // Do any additional setup after loading the view.

    self.view.addGestureRecognizer(UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing(_:))))

    let datePicker = UIDatePicker()
    datePicker.datePickerMode = UIDatePickerMode.date
    datePicker.addTarget(self, action: #selector(RegisterViewController.datePickerValueChanged(sender:)), for: UIControlEvents.valueChanged)
    dobField.inputView = datePicker

    self.profileImage.layer.cornerRadius = self.profileImage.frame.size.width / 2;
    self.profileImage.clipsToBounds = true;



@objc func datePickerValueChanged(sender: UIDatePicker) 
    let formatter = DateFormatter()
    formatter.dateStyle = DateFormatter.Style.medium
    formatter.timeStyle = DateFormatter.Style.none
    dobField.text = formatter.string(from: sender.date)



@IBAction func selectImageTapped(_ sender: UIButton) 
    let picker = UIImagePickerController()
    picker.delegate = self
    self.present(picker, animated: true, completion: nil)


func uploadImage(image: UIImage) 
    let randomName = randomStringWithLength(length: 10)
    let imageData = UIImageJPEGRepresentation(image, 1.0)
    let uploadRef = Storage.storage().reference().child("images/profimg/\(randomName).jpg")
    let uploadTask = uploadRef.putData(imageData!, metadata: nil)  metadata,
        error in
        if error == nil 
            //success
            print("success")
            self.imageFileName = "\(randomName as String).jpg"
         else 
            //error
            print("error uploading image")
        
    


func randomStringWithLength(length: Int) -> NSString 
    let characters: NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    let randomString: NSMutableString = NSMutableString(capacity: length)

    for i in 0..<length 
        var len = UInt32(characters.length)
        var rand = arc4random_uniform(len)
        randomString.appendFormat("%C", characters.character(at: Int(rand)))
    
    return randomString


func imagePickerControllerDidCancel(_ picker: UIImagePickerController) 
    // will run if the user hits cancel
    picker.dismiss(animated: true, completion: nil)


@objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) 
    // will run when the user finishes picking an image from the library
    if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage 
        self.profileImage.image = pickedImage
        self.selectImageButton.isEnabled = false
        self.selectImageButton.isHidden = true
        uploadImage(image: pickedImage)
        picker.dismiss(animated: true, completion: nil)
    


override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.


@IBAction func registerTapped(_ sender: UIButton) 

    let username = usernameField.text
    let email = emailField.text
    let password = passwordField.text
    let dob = dobField.text

    Auth.auth().createUser(withEmail: email!, password: password!)  (user, error) in
        if error != nil 
            //error creating account
            let alert = UIAlertController(title: "Error", message: "An error occurred when creating your account, please try again.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            self.present(alert, animated: true, completion: nil)
        else 
            //account created

            if (self.imageFileName != "") 
                if let uid = Auth.auth().currentUser?.uid 

                    let regObject: Dictionary<String, Any> = [
                        "uid" : uid,
                        "username" : username,
                        "dateofbirth" : dob,
                        "profimage" : self.imageFileName
                    ]
                Database.database().reference().child("posts").child(uid).setValue(regObject)

                    let vc = self.storyboard?.instantiateViewController(withIdentifier: "LoggedInVC")
                    self.present(vc!, animated: true, completion: nil)
                else 
                    //image hasnt finished uploading
                    let alert = UIAlertController(title: "Please wait", message: "Your image has not finished uploading yet, please wait...", preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                    self.present(alert, animated: true, completion: nil)
                
            

            //let alert = UIAlertController(title: "Success!", message: "Account has been created...", preferredStyle: .alert)
            //alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
            //self.present(alert, animated: true, completion: nil)

        
    


/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) 
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.

*/


如果你们中的任何人能指出我正确的方向,或者能够告诉我我的代码中哪里出了问题,那就太好了。我预计不会直接解决我的问题,所以任何事情都会有所帮助。

谢谢!

【问题讨论】:

我还应该补充一点,虽然我在创建帐户时遇到问题 - Firebase 仍会存储用户的电子邮件和密码,但不会写入其余数据或上传图像。 您想在用户 Firebase 帐户中存储个人资料图片,还是在 Firebase 存储中存储图片,还是在 Firebase 数据库中存储小图片? 您好,感谢您的关心,不过,在 dahiya_boy 的帮助下,我已经解决了这个问题。 【参考方案1】:

将 img 上传到 firebase 存储

func uploadImagePic(img1 :UIImage)
        var data = NSData()
        data = UIImageJPEGRepresentation(img1!, 0.8)! as NSData
        // set upload path
        let filePath = "\(userid)" // path where you wanted to store img in storage
        let metaData = FIRStorageMetadata()
        metaData.contentType = "image/jpg"

        self.storageRef = FIRStorage.storage().reference()
        self.storageRef.child(filePath).put(data as Data, metadata: metaData)(metaData,error) in
            if let error = error 
                print(error.localizedDescription)
                return
            else
                //store downloadURL
                let downloadURL = metaData!.downloadURL()!.absoluteString

            
        

    

【讨论】:

您好,我收到错误命名文件路径“对象聊天不存在”【参考方案2】:

为了从上传的文件中获取下载 url,根据答案,metaData 中的downloadURL 现在已被弃用,所以这是正确的方法:

 storageRef.downloadURL(completion:  (url: URL?, error: Error?) in
    print(url?.absoluteString) // <- Your url
 )

您应该从刚刚创建的引用中获取下载 url,您可以在其中找到带有完成处理程序的 downloadURL

这是一个更新的 Swift 5 答案:

func uploadImagePic(image: UIImage, name: String, filePath: String) 
    guard let imageData: Data = image.jpegData(compressionQuality: 0.1) else 
        return
    

    let metaDataConfig = StorageMetadata()
    metaDataConfig.contentType = "image/jpg"

    let storageRef = Storage.storage().reference(withPath: filePath)

    storageRef.putData(imageData, metadata: metaDataConfig) (metaData, error) in
        if let error = error 
            print(error.localizedDescription)

            return
        

        storageRef.downloadURL(completion:  (url: URL?, error: Error?) in
            print(url?.absoluteString) // <- Download URL
        )
    

【讨论】:

以上是关于使用 Firebase 和 Swift (iOS) 上传图片的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Swift 5 (iOS) 从 Firebase 数据库中获取和打印单个值

使用firebase问题推送通知(ios,swift 4)[关闭]

如何使用swift和ios使firebase sdk中的旧消息过期

在 iOS 上使用 Swift 多次调用 Firebase 'Observe'

Firebase、iOS 和 Swift 入门

Firebase iOS Swift - 将 Atname 更新功能添加到设置