如何以正确的方式从 firebase 数据库加载图像

Posted

技术标签:

【中文标题】如何以正确的方式从 firebase 数据库加载图像【英文标题】:How do I load image from firebase database the correct way 【发布时间】:2019-12-28 08:37:25 【问题描述】:

我有一个 -post 和 -user 的数据库结构,在 post 下我有发布信息,即发布图像、文本数据和发布它的用户的 uid。在用户节点中,我有用户信息,其中包括用户的个人资料图像。

现在我正在尝试检索帖子以及用户的个人资料图片。我能够检索帖子但无法正确加载个人资料图片,我尝试了很多不同的方法,代码中的一个有 2 个索引,当计数相等时重新加载 tableview,这会产生冲突并加载图像以随机方式,即使计数相同。任何想法我都可以加载图像,以便它们对应于正确的帖子。我是火力基地的新手。

func postRetrieve()  
SVProgressHUD.show()
let ref = Database.database().reference()
ref.child("posts").queryOrdered(byChild: "timesStamp").observeSingleEvent(of: .value)  (snapshot) in
    let post = snapshot.value as! [String : AnyObject]
    self.userPost.removeAll()

    for (_ , value ) in post 
        if let usersId = value["userID"] as? String  
            self.usersKey.append(usersId)
            print("here toooo")
            let finalPostToShow = Post()
            if let topicData = value["topic"] as? String,
                let userName = value["creator"] as? String,
                let timeStamp = value["timesStamp"] as? Double

                
                finalPostToShow.topicData = topicData
                finalPostToShow.usrId = usersId
                finalPostToShow.userName = userName

                self.userPost.append(finalPostToShow)
            
        
        print("there are \(self.userPost.count) posts")

    
     guard self.userPost.count != self.userCred.count else 
            print("loading cells now 01")
            return self.TableViewControllerHome.reloadData()

ref.removeAllObservers()

加载配置文件图像的代码单独调用:

func profileImage() 
    let ref = Database.database().reference()
    ref.child("users").queryOrdered(byChild: "timesStamp").observeSingleEvent(of: .value)  (dataSnapshots) in
        let usersnap = dataSnapshots.value as? [String : AnyObject]
        self.userCred.removeAll()
        for (_,value) in usersnap! 
            if let userIdKey = value["uid"] as? String 
                let finalUserToShow = user()
                for each in self.usersKey 
                    if each == userIdKey 
                        if let userPhotoImg = value["urlToImage"] as? String
                            finalUserToShow.ImagePath = userPhotoImg
                            self.userCred.append(finalUserToShow)
                        
                    
                    print(finalUserToShow)
                    print("there are   \(self.userCred.count) profile photos")
                
            
        
    

我的火力基地结构:

"posts" : 
"-LwxPtjQh6Sq9cMw-XSO" : 
  "attachedUsers" : [ "BtJEpoob3CPR1EN1nFRiUj6uKIX2", "OtipKVdgmyNNR9eGwVFBqyYrvL93", "PV3NEJhepRZQiNHJ7H7KO9aWGy62", "wt4PZb6UUtbeUI2v2SFRZ9hZBNt1" ],
  "content" : "The 2019 Range Rover Autobiography delivers 16 mpg in ",
  "creator" : "Mac",
  "description" : "Rangerover  ",
  "likes" : 0,
  "pathToImage" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/posts%2Fssb9HIqEp4SxPuUZSJDUfYYLKPS2%2F-LwxPtjQh6Sq9cMw-XSO.jpeg?alt=media&token=8a87777f-d2c4-4fb9-8c7d-89b5fb0ef042",
  "timesStamp" : 1577283495118,
  "topic" : "Rangerover",
  "userID" : "ssb9HIqEp4SxPuUZSJDUfYYLKPS2",
  "views" : 0,
  "webUrl" : "https://www.landrover.in/index.html"

    "-Lx0z-1LlbIhpBFGNIfW" : 
  "attachedUsers" : [ "X0kiMno8EEclwlVvVymNK00wNw52", "OtipKVdgmyNNR9eGwVFBqyYrvL93", "38nPNJujqpeovv558FeFHqB90Qu2", "0NQbltVlx3d3yOwich9IXntyBqD3" ],
  "content" : "The  been",
  "creator" : "Mac",
  "description" : "Cybertruck ",
  "likes" : 0,
  "mainImages" : 
    "-Lx0z0_Nc6FIA_flu_Eq" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/posts%2Fssb9HIqEp4SxPuUZSJDUfYYLKPS2%2F-Lx0z0_Nc6FIA_flu_Eq.jpeg?alt=media&token=becde9e1-ca9d-4e98-98ce-e1aa5edeede2",
    "-Lx0z0_T1uxgmU8DCVda" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/posts%2Fssb9HIqEp4SxPuUZSJDUfYYLKPS2%2F-Lx0z0_T1uxgmU8DCVda.jpeg?alt=media&token=3b71bca8-8988-481f-994c-41e07fde6972"
  ,
  "pathToImage" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/posts%2Fssb9HIqEp4SxPuUZSJDUfYYLKPS2%2F-Lx0z-1LlbIhpBFGNIfW.jpeg?alt=media&token=0108e73d-3e34-4137-8729-7e826166d871",
  "timesStamp" : 1577360038766,
  "topic" : "Madness",
  "userID" : "ssb9HIqEp4SxPuUZSJDUfYYLKPS2",
  "views" : 0,
  "webUrl" : ""
,




"users" : 
"0NQbltVlx3d3yOwich9IXntyBqD3" : 
  "dateJoined" : 1541925226084,
  "followers" : 
    "-LR2KlE95CjaV6NLh36e" : "DjyEuBBc3YRGq8lZ1AmDxtUOUNq1",
    "-LRP2W1XXhB1oerC2LJu" : "2EWMigIqM2hObmRQqFgR60T9L5r1",
    "-LTHvpSl8tcVWJs8Gyef" : "9zqI9O8uU1VdeysJ8p0F51AVQG13",
    "-LTTrN2UxHRMkaoZdOJj" : "jeCBp7JvXhhV1hyBCW6pGSdJWxo2",
    "-Lf_gGCB_1nWpfQNIf6d" : "38nPNJujqpeovv558FeFHqB90Qu2",
    "-LtPH3HCtgkT5j2Xq7Hk" : "ssb9HIqEp4SxPuUZSJDUfYYLKPS2"
  ,
  "full Name" : "Warren Buffett",
  "uid" : "0NQbltVlx3d3yOwich9IXntyBqD3",
  "urlToImage" : "https://firebasestorage.googleapis.com/v0/b/neighbors-3a1df.appspot.com/o/users%2F0NQbltVlx3d3yOwich9IXntyBqD3.jpg?alt=media&token=673f8326-870e-496c-89ee-3ad9c06693ca"
,

加载图片的函数:

class CustomImageView : UIImageView 
func loadImagesWithUrl(from imageUrl : String!)  
    self.image = nil

    if let cachedImage = imageCache.object(forKey: imageUrl as AnyObject) as? UIImage 
        self.image = cachedImage
        return
    

    let url = URLRequest(url: URL(string: imageUrl)!)
    URLSession.shared.dataTask(with: url)  (data, response, eror) in

        if eror != nil 
            print(eror!)
            return
        

            DispatchQueue.main.async 
            if let downloadedImage = UIImage(data : data!) 
                imageCache.setObject(downloadedImage, forKey: imageUrl as AnyObject)
                self.image = downloadedImage
            
        
    .resume()

我的表格视图 cellForRowAt :

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
   let cellData =  TableViewControllerHome.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! viewControllerHomeCell

   cellData.writingfrom.setTitle(self.userPost[indexPath.row].topicData, for: .normal)
    cellData.profileImage.loadImagesWithUrl(from:self.userCred[indexPath.row].ImagePath)
    cellData.profileName.text = userPost[indexPath.row].userName
    cellData.dateCreated.text = userPost[indexPath.row].dateCreated
    cellData.userID = userPost[indexPath.row].usrId


    return cellData

【问题讨论】:

加载图像的问题没有代码,我们需要看到它,因为它是问题的一部分。第二个ref.removeAllObservers() 不需要(在这种情况下),因为您没有添加观察者; .observeSingleEvent 触发一次且仅触发一次。我们真的需要看看你的 Firebase 结构;请在您的问题中包含一个作为 TEXT 的 sn-p。最后,提示:如果您将用户存储在用户节点中,并将其 uid 作为键,则不需要此 if let userIdKey = value["uid"],因为 snapshot.key 将为您提供用户的 uid。更新问题,我们来看看! @jay 更新了好友 我的评论中最重要的问题似乎被忽略了:加载图像的问题中没有代码。包含的代码只是将 URL 分配给用户类中的 var,仅此而已。没有它,我们将不知道实际加载图像的顺序是什么,这是解决您的问题所必需的: @jay 哦!我忽略了这一点,现在更新问题 【参考方案1】:

这个答案有两个部分:一个是展示如何处理数据流的示例代码。第二个是展示如何大大缩短和收紧你的代码,因为问题中有很多无关的代码。

在您处理帖子的问题中 - 这个答案几乎是一样的;我们将遍历我们的用户节点并将用户名和个人资料图像加载到 tableView 中。名称存储在 Firebase 实时数据库中,图像存储在 Firebase 存储中(我强烈建议使用它,因为 API 很棒)。

让我们从一个存储用户名(例如您的帖子文本)的类开始,然后是个人资料图片。请注意,在某些情况下图像可能为零,因此我们需要在 tableView 刷新时处理这些情况。

class UserNameAndImageClass 
    var uid = ""
    var name = ""
    var image: NSImage?

    init(withName: String, andUid: String, andImage: NSImage?) 
        self.name = withName
        self.uid = andUid
        self.image = andImage
    

然后是一个类级别的 var 数组,用作 tableView 的数据源

var userNameAndImageArray = [UserNameAndImageClass]()

然后是读取所有数据并填充dataSouce的代码,完成后重新加载tableView

func fetchUsersAndImages() 
    let storage = Storage.storage()
    let storageRef = storage.reference()
    let imagesRef = storageRef.child("images") //where the profile images are stored

    let usersRef = self.ref.child("users")
    usersRef.observeSingleEvent(of: .value, with:  snapshot in
        let lastIndex = snapshot.childrenCount - 1 //how many total child nodes
        var currentIndex = 0 //used to trigger the reload when all data has been read

        //Assign all of the child nodes to an array of DataSnapshots. This keeps them in
        //  order and also maintains each child node as a DataSnapshot making it easier to
        //  access other child data within each node
        let allUsersSnap = snapshot.children.allObjects as! [DataSnapshot]                                                
        for userSnap in allUsersSnap 
            let uid = userSnap.key
            let name = userSnap.childSnapshot(forPath: "name").value as? String ?? "No Name"
            let profileImageName = "\(uid).png"
            let picRef = imagesRef.child(profileImageName)

            //Firebase Storage code here - insert your own if needed
            picRef.getData(maxSize: 1 * 1024 * 1024)  data, error in // Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
                var aImage: NSImage?
                if let imageData = data  //data could be nil
                    aImage = NSImage(data: imageData)
                

                let aUser = UserNameAndImageClass(withName: name, andUid: uid, andImage: aImage)
                self.userNameAndImageArray.append(aUser)

                if currentIndex == lastIndex  //if we've loaded all of the users and their images, refresh the UI
                    self.myTableView.reloadData()
                

                currentIndex += 1
            
        
    )

有几点需要注意。

在您的用户节点中,您将用户 uid 存储为节点的键以及子节点。没有理由这样做 - 只需将其存储为密钥!如果您读取该节点,则始终可以像这样使用 snapshot.key 访问密钥

let uid = userSnap.key

在图片中读取的部分,请注意我使用的是一个简单的计数器,它会等到我们读取最后一张图片后才更新 UI。它们会减少闪烁。有些人会建议使用调度组来完成这项任务,这也可以。

你不需要在使用 .observeSingleEvent 后删除AllObservers,因为它只会触发一次并且不会留下观察者。

最后也是最重要的一点:不要在 tableView 函数中做任何繁重的工作!它们应该是快速、轻量级的,并且代码极简。在单元格或 tableView 函数中从 Internet 检索数据通常不是一个好主意,因为这会使应用程序滞后和糟糕的用户体验。预加载所有数据以保持 tableview 流畅。

【讨论】:

帮助很大!您展示了如何更有效、更准确地从 firebase 数据库加载数据,它几乎解决了这个问题。但是,假设用户像在 instagram 中一样上传帖子,创建了一个单独的“帖子”节点,我可以轻松地从帖子中获取数据,除了上传帖子的用户的个人资料图片不在“帖子”中node ,而是在单独的“用户”节点中。那么,我如何从两个不同的节点获取数据并将它们显示在一个 tableviewcell 中。 @mac 非常简单。您将从帖子中获取该用户的 uid。然后从用户节点中提取 url 并按照我的回答进行填充。换句话说,在let uid = userSnap.key 之后,使用该uid 并在该闭包内的用户节点上观察SingleEvent,获取图像的url,然后通过该url 检索图像。如果结构变得太深,您可能需要对其进行非规范化,但听起来并非如此。

以上是关于如何以正确的方式从 firebase 数据库加载图像的主要内容,如果未能解决你的问题,请参考以下文章

从 Firebase 完成加载后如何使 UIRefreshControl 结束刷新

检索 Firebase 快照导致加载验证错误

如何从 Firebase 获取 PWA 数据

如何初始加载 UITableView 然后观察 Firebase 节点以更改在 Swift 中刷新 tableview?

如何在flutter中以排序方式从firebase数据库中检索数据..当我将其检索为Map时出现问题

从 Firebase 获取数据以执行拉取刷新和加载更多功能