Firebase Storage Image Cache не обновляет SWIFT - PullRequest
5 голосов
/ 25 июня 2019

В моем приложении настроены образы для работы с пользовательским интерфейсом Firebase, так что я могу легко их кэшировать, следуя документам 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. Я создаю ссылку на хранилище для использования с SDWebImage на основе пользовательского идентификатора пользователя. Поэтому ссылка на хранилище не меняется, что является причиной проблемы.

Ответы [ 2 ]

3 голосов
/ 25 июня 2019

Когда вы загружаете изображение с помощью SDWebImage, оно кэширует изображение навсегда и не будет запрашивать его из сети в следующий раз. при загрузке нового изображения с другого устройства URL-адрес изображения остается прежним, т. е. SDWebImage на предыдущем устройстве не знает, что изображение было обновлено. Поэтому вам нужно придумать, как уведомить предыдущее устройство об изменении.

ниже, как я думаю, это должно быть сделано:

При загрузке изображения используйте динамическое имя изображения. так что URL-адрес изменяется, поэтому SDWebImage загрузит новое изображение профиля.


В комментариях вы спросили, как бы я сослался на этот динамический URL. У вас есть два возможных варианта (которые я могу придумать):

1) с использованием базы данных firebase : В этом мы создадим узел / ссылку на базу данных, как user / uid. В этом мы будем хранить динамический imageurl с ключевым образом. на любом устройстве, когда вы хотите загрузить изображение на устройство, вы должны прочитать URL из базы данных. Он всегда будет указывать на новый URL-адрес изображения. Хороший пример того, как это сделать, - SO POST

Краткая информация: Пользователь-> Загрузить изображение-> Сохранить URL в базе данных

Чтение URL-адреса изображения из Firebase -> Загрузка его через SDWebImage UIImageView

2) Использование текстового файла : В этом мы не будем использовать базу данных. мы создадим файл журнала в той же папке users / uid / log.txt. всякий раз, когда вы загружаете изображение в хранилище, мы сохраняем URL-адрес в этом текстовом файле и также загружаем его в хранилище. Когда вы хотите загрузить изображение, вы сначала читаете текстовый файл, чтобы получить imageUrl, а затем загружаете изображение с помощью SDWebImage. Этот способ очень похож на загрузку и загрузку файла изображения с одним дополнительным шагом, который вы должны прочитать текстовый файл, чтобы получить хранилище.

Краткое описание: Пользователь-> Загрузить изображение -> Поместите URL-адрес в текстовый файл и загрузите его тоже

Загрузка текстового файла -> чтение URL-адреса изображения из файла -> загрузка изображения через SDWebImage в UIImageVIew


Я читал документы и нашел другой способ. Таким образом, вам не нужно использовать динамический URL. Вы можете использовать свой статический URL. Для этого вам нужно запросить метаданные изображения:

// 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.

Взято с

Каждый раз, когда вы запрашиваете метаданные, сохраняйте timeCreated в UserDefaults и в следующий раз сравнивайте timeCreated с сохраненным timeCreated, если есть разница, удалите изображение из SDWebImage Cache, а затем попросите SDWebImage снова загрузить изображение.

1 голос
/ 05 июля 2019

Причина, по которой ваш кэш не обновляется, заключается в том, что вы всегда пишете по одному и тому же пути, вы просто загружаете другое изображение.Я вижу, что вы спрашивали о создании динамического 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) и снова его кэшируете.

    /// 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)
          })
        }
      }
    }

У меня есть кнопка, которая отображает мое изображение профиля в виде изображения.Когда я получаю новое изображение и вызываю метод для обновления вида кнопки, изображение выбирается, и, поскольку кэш больше не имеет ссылки на путь к изображению, он извлекает вновь загруженное изображение и воссоздает путь к кэшу.

Вот так я и сделал эту работу для меня.Я надеюсь, что это решит и вашу проблему.Не стесняйтесь спрашивать о любых дополнительных разъяснениях, и я сделаю все возможное, чтобы ответить.

Редактировать: Сахиль Манчанда указал мне, что я неправильно понял весь объем вопроса.

Мой ответ будет лучше всего работать на одном устройстве.

Если проблема связана с синхронизацией нескольких устройств, вы все равно можете использовать предлагаемое решение, но с некоторыми более частыми обновлениями пользовательского интерфейса - поскольку свойство profilePictureUrl обновляется каждый раз, когда информация пользователя извлекается из Firebase, обновление может бытьзадерживается до следующего чтения информации пользователя или перезапуска приложения.

Вы также можете добавить проверку подключения перед удалением кэша, чтобы избежать потерь, вызванных задержкой пропускной способности.

Другим решением может быть использование тихих уведомлений (предназначенных для операций быстрого обновления), которые будут просто напоминать устройству, что пора очищать кэш.Вы можете использовать Firebase Cloud Messaging , если у вас нет опыта работы с серверами или OneSignal.

...