我在应用程序上安装了可与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一起使用的存储引用。因此,存储参考未更改,这引起了问题。
答案 0 :(得分:3)
使用SDWebImage加载图像时,它将永久缓存该图像,下次将不再从网络请求该图像。当您从其他设备上载新图像时,该图像的网址保持不变,即先前设备上的SDWebImage不知道该图像已更新。因此,您需要提出有关如何将更改通知给先前设备的想法。
以下是我认为应做的事情:
上载图像时,请使用动态图片名称。以便更改网址,因此SDWebImage将加载新的个人资料图片。
在评论中,您问我如何引用此动态URL。您有两种可能的选择(我能想到):
1)使用Firebase数据库: 在此,我们将在数据库中创建一个节点/引用,例如用户/ uid。在这里,我们将动态图像URL与关键图像一起存储。在任何设备上,当您要将图像加载到设备上时,都将从数据库中读取URL。它总是指向新的图片网址。SO POST
上是一个很好的例子。首字母缩写: 用户->上传图像->将网址保存到数据库
从Firebase读取图片网址->通过SDWebImage UIImageView加载图片网址
2)使用文本文件: 在此我们将不使用数据库。我们将在同一文件夹users / uid / log.txt文件下创建一个日志文件。每当您将图片上传到存储设备时,我们都会将网址保存在此文本文件中,并将其也上传到存储设备中。当您要加载图像时,首先要读取文本文件以获取imageUrl,然后使用SDWebImage加载图像。这种方式与上传和下载图像文件非常相似,但又需要额外的步骤,即您必须阅读文本文件以获取存储空间。
简述:用户->上传图片->将网址放入文本文件中并上传它
下载文本文件->从文件中读取图像网址->通过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'
}
}
文件元数据包含常用属性,例如名称,大小和 contentType(通常称为MIME类型)除一些 常见的诸如contentDisposition和timeCreated。
每当您查询元数据时,请将UserCreates中的timeCreated保存下来,然后下次将timeCreated与保存的timeCreated进行比较,如果存在差异,请从SDWebImage缓存中删除图像,然后要求SDWebImage重新加载该图像。
答案 1 :(得分:1)
您的缓存不刷新的原因是因为您始终在写入同一路径,而只是上传不同的图像。我看到您一直在问有关创建动态URL的问题,从理论上讲,该URL将创建未缓存的图像路径,因此可以正确地重新加载。
但是您不需要这样做。在使用Firebase的应用程序上工作时,我遇到了同样的情况和问题。我有一个播放器模型,其中包含对个人资料图片的引用。图片是使用Firebase存储存储的,文件夹路径为/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)
并再次将其缓存时,它会这样做。
/// 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的Cloud Messaging。
答案 2 :(得分:0)
这是我解决此问题的方法:
它从存储中检索元数据,并将服务器上的创建日期与缓存图像的创建日期进行比较。如果缓存过期,它将从服务器重新缓存图像。
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)
}
}
}
}
}
}
答案 3 :(得分:0)
我遇到了同样的问题并使用最新版本解决了它,所以我想在这里添加我的答案以帮助其他人。
SDWEBImage 为那些将在相同 URL 或路径上更新的图像更新了新功能。
功能详情摘要:
<块引用>即使图片被缓存,也要尊重HTTP响应缓存控制, 并在需要时从远程位置刷新图像。磁盘 缓存将由 NSURLCache 而不是 SDWebImage 处理,导致 轻微的性能下降。此选项有助于处理图像 在同一请求 URL 后面更改,例如Facebook 图 API 配置文件 图片。如果刷新缓存图像,则调用完成块 一次使用缓存图像,再次使用最终图像。声明
SDWebImageRefreshCached = 1 << 3
仅当您无法使用嵌入的 URL 使您的 URL 成为静态时才使用此标志 缓存破坏参数。
声明于:SDWebImageDefine.h
所以简而言之,如果我们想更新缓存的图像,该图像将来会在同一 URL 上更新,我们可以使用以下方法。
带有完成块的图像 URL:
imageView.sd_setImage(with: <URL>,
placeholderImage: placeholderImage,
options: .refreshCached)
{ (image, error, type, url) in
<CODE>
}
没有完成块的图片网址:
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 中,让我们的生活更轻松。
希望这个答案可以帮助其他人。