簡體   English   中英

Firebase 存儲圖像緩存未更新 SWIFT

[英]Firebase Storage Image Cache not updating SWIFT

我在我的應用程序上設置了圖像以使用 firebase UI,以便我可以通過遵循文檔https://firebase.google.com/docs/storage/ios/download-files輕松緩存它們

我的代碼看起來像這樣

    guard let profileImageUrl = user?.url else { return }
    profileImageView.sd_setImage(with: profileImageUrl)

我正在覆蓋圖像位置以更新這樣的圖像

let storageRef = Storage.storage().reference(withPath: "/users/\(uid)/profilePhoto.jpg")
storageRef.putData(uploadData, metadata: nil, completion: { (metadata, err) in

如果我在沒有緩存的新設備上訪問照片,它會顯示新圖像。 但是,如果我在以前查看過的設備上訪問該圖像,則會看到舊圖像。

編輯:所以我了解緩存是通過 SDWebImage 工作的。 我正在根據用戶 uid 生成與 SDWebImage 一起使用的存儲引用。 因此,存儲引用不會改變,這會導致問題。

當您使用 SDWebImage 加載圖像時,它會永久緩存圖像並且下次不會從網絡請求它。 當您從另一台設備上傳新圖像時,圖像的 url 保持不變,即之前設備上的 SDWebImage 不知道圖像已更新。 因此,您需要想出如何將更改通知先前設備的想法。

以下是我認為應該如何完成:

上傳圖片時,請使用動態圖片名稱。 以便更改 url,因此 SDWebImage 將加載新的個人資料圖片。


在評論中,您問我將如何引用此動態 url。 你有兩種可能的選擇(我能想到的):

1) 使用 firebase 數據庫:在這里,我們將在用戶/uid 等數據庫上創建一個節點/引用。 在這里,我們將存儲帶有關鍵圖像的動態 imageurl。 在任何設備上,當您想在設備上加載圖像時,您將從數據庫中讀取 url。 它總是指向新的圖像 url 關於如何做到這一點的一個很好的例子是在這個SO POST

Breif:用戶-> 上傳圖像-> 將 URL 保存到數據庫

從 Firebase 讀取圖片 URL -> 通過 SDWebImage UIImageView 加載

2) 使用文本文件:在此我們將不使用數據庫。 我們將在同一文件夾 users/uid/log.txt 文件下創建一個日志文件。 每當您將圖像上傳到存儲時,我們都會將 url 保存在此文本文件中並將其上傳到存儲中。 當您要加載圖像時,首先讀取文本文件以獲取 imageUrl,然后使用 SDWebImage 加載圖像。 這種方式與上傳和下載圖像文件非常相似,只需一個額外的步驟,您必須讀取文本文件以獲取存儲空間。

簡介:用戶->上傳圖片->將url放入文本文件並上傳

下載文本文件 -> 從文件中讀取圖像 url -> 通過 SDWebImage 將圖像加載到 UIImageVIew


我正在閱讀文檔並找到了另一種方法。 這樣您就不需要使用動態網址。 您可以使用您的靜態網址。 在此您需要查詢圖像的元數據:

// Create reference to the file whose metadata we want to retrieve
let forestRef =  Storage.storage().reference(withPath: "/users/\(uid)/profilePhoto.jpg")

// Get metadata properties
forestRef.getMetadata { metadata, error in
  if let error = error {
    // Uh-oh, an error occurred!
  } else {
    // Metadata now contains the metadata for 'profilePhoto.jpg'  
  }
}

除了一些不太常見的屬性(如 contentDisposition 和 timeCreated)之外,文件元數據還包含常見的屬性,例如名稱、大小和 contentType(通常稱為 MIME 類型)。

取自

每當您查詢元數據時,將 timeCreated 保存在 UserDefaults 中,下次將 timeCreated 與保存的 timeCreated 進行比較,如果存在差異,則從 SDWebImage Cache 中刪除圖像,然后要求 SDWebImage 再次加載圖像。

您的緩存不刷新的原因是因為您總是寫入相同的路徑,您只是上傳不同的圖像。 我看到您一直在詢問創建一個動態 URL,理論上,它會創建一個未緩存的圖像路徑,因此可以正確重新加載。

但你不需要這樣做。 我最近在開發使用 Firebase 的應用程序時遇到了同樣的情況和問題。 我有一個播放器模型,其中包含對個人資料圖片的引用。 圖片是使用 Firebase Storage 存儲的,文件夾路徑為/players/\\(uid)/profilePhoto.jpg 可以通過購買頭像和使用相機拍攝照片來更改個人資料圖片。 而且由於我每次都上傳到相同的存儲路徑,因此我的圖像也沒有重新加載。

現在,SDWebImage 緩存是自動的 - 除非您指定不同,每當您調用someImageView.sd_setImage(with: imageUrl) ,URL 都會通過 SDImageCache 緩存。

所以我所做的就是在獲得新參考時從緩存中刪除圖像 - 在個人資料圖片的didSet屬性觀察者中。

    /// property to store the profile picture url
    internal var profilePictureUrl: String = "" {
      didSet {
        SDImageCache.shared().removeImage(forKey: Storage.storage().reference().child(profilePicture).fullPath, withCompletion: nil)
      }
    }

現在,每次設置屬性時,都會從緩存中刪除 url,SDWebImage 必須再次獲取它。 當您調用sd_setImage(with: imageUrl)並再次緩存它時,它sd_setImage(with: imageUrl)

    /// public property to set the profile image on the button
    internal var profileImagePath: String? {
      didSet {
        if let path = profileImagePath {
          profileButton.imageView?.sd_setImage(with: Storage.storage().reference().child(path), placeholderImage: nil, completion: { [weak self] (image, error, _, _) in
            self?.profileButton.setImage(image ?? #imageLiteral(resourceName: "default-profile-pic"), for: .normal)
          })
        }
      }
    }

我有一個按鈕,可以在其圖像視圖中顯示我的個人資料圖片。 當我獲取新圖像並調用刷新按鈕視圖的方法時,將獲取圖像,並且由於緩存不再引用圖像路徑,它將獲取新上傳的圖像並重新創建緩存路徑。

這就是我為我完成這項工作的方式。 我希望它也能解決您的問題。 請隨時提出任何其他說明,我會盡力回答。

編輯: Sahil Manchanda向我指出我誤解了問題的全部范圍。

我的答案在單個設備上效果最佳。

如果問題是多設備同步,您仍然可以使用提供的解決方案,但需要更頻繁的 UI 更新 - 由於每次從 Firebase 獲取用戶信息時都會更新profilePictureUrl屬性,因此更新可能會延遲到下一次用戶信息時已讀取或應用程序已重新啟動。

您還可以在刪除緩存之前添加連接檢查,以避免因帶寬滯后造成的任何損失。

另一種解決方案可能是使用靜默通知(用於快速刷新操作),它僅作為設備“提醒”該清除緩存的時間。 如果您對服務器或 OneSignal 沒有任何經驗,則可以使用 Firebase 的雲消息傳遞

一段時間后,我找到了另一種更好的方法,因為它比我以前的解決方案只使用一個網絡請求。 現在我正在使用直接 url 在存儲桶中進行圖像處理。 然后我使用 Nuke(圖像加載和緩存庫)加載它,這尊重原生 HTTP 緩存機制。


extension StorageReference {
    var url: URL {
        let path = fullPath.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
        return URL(string: "https://firebasestorage.googleapis.com/v0/b/\(bucket)/o/\(path)?alt=media")!
    }
}

Nuke.loadImage(with: imgRef.url, into: imageView)

我的舊答案:

它從存儲檢索元數據,並將服務器上的創建日期與緩存圖像的創建日期進行比較。 如果緩存過時,它將從服務器重新緩存圖像。

extension UIImageView {

    func setImage(with reference: StorageReference, placeholder: UIImage? = nil) {
        sd_setImage(with: reference, placeholderImage: placeholder) { [weak self] image, _, _, _ in
            reference.getMetadata { metadata, _ in
                if let url = NSURL.sd_URL(with: reference)?.absoluteString,
                    let cachePath = SDImageCache.shared.cachePath(forKey: url),
                    let attributes = try? FileManager.default.attributesOfItem(atPath: cachePath),
                    let cacheDate = attributes[.creationDate] as? Date,
                    let serverDate = metadata?.timeCreated,
                    serverDate > cacheDate {

                    SDImageCache.shared.removeImage(forKey: url) {
                        self?.sd_setImage(with: reference, placeholderImage: image, completion: nil)
                    }
                }
            }
        }
    }

}

我遇到了同樣的問題並用最新版本解決了它,所以想在這里添加我的答案以幫助其他人。

SDWEBImage 更新了這些圖像的新功能,這些圖像將在相同的 URL 或路徑上更新。

功能細節總結:

即使圖像被緩存,也要遵守 HTTP 響應緩存控制,並在需要時從遠程位置刷新圖像。 磁盤緩存將由 NSURLCache 而不是 SDWebImage 處理,導致性能略有下降。 此選項有助於處理在同一請求 URL 后面更改的圖像,例如 Facebook 圖形 API 配置文件圖片。 如果刷新緩存圖像,則使用緩存圖像和最終圖像再次調用完成塊。 宣言

SDWebImageRefreshCached = 1 << 3

僅當您無法使用嵌入式緩存破壞參數使 URL 成為靜態時,才使用此標志。

聲明於:SDWebImageDefine.h

所以簡而言之,如果我們想更新緩存的圖像,該圖像將來會在同一 URL 上更新,我們可以使用以下方法。

帶有完成塊的圖像 URL:

imageView.sd_setImage(with: <URL>,
                      placeholderImage: placeholderImage,
                      options: .refreshCached)
                { (image, error, type, url) in
                    <CODE>
                }

沒有完成塊的圖像 URL:

imageView.sd_setImage(with: <URL>,
                      placeholderImage: placeholderImage,
                      options: .refreshCached)

帶有完成塊的 Firebse 參考:

imageView.sd_setImage(with: reference,
                      maxImageSize: <MaxSize>,
                      placeholderImage: placeholderImage,
                      options: .refreshCached)
                { (image, error, type, ref) in
                    <#code#>
                }

沒有完成塊的 Firebse 參考:

imageView.sd_setImage(with: reference,
                      maxImageSize: <MaxSize>,
                      placeholderImage: placeholderImage,
                      options: .refreshCached)

感謝 SDWebImage 團隊將這個驚人的功能添加到 lib 中,讓我們的生活變得輕松。

希望這個答案可以幫助其他人。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM