Источник и кэширование изображений

Я использую следующий код для отображения изображения с веб-сервера:

   <Image Source="{Binding Url}" />

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

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

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

5 ответов


BitmapImage автоматически кэширует удаленные изображения по умолчанию. Лучше всего использовать в сочетании с CreateOptions="BackgroundCreation" для лучшей производительности.

<Image Height="100" Width="100" Margin="12,0,9,0">
  <Image.Source>
    <BitmapImage UriSource="{Binding ImgURL}" CreateOptions="BackgroundCreation"/>
  </Image.Source>
</Image>

это сообщение в блоге MSDN, старые, но все еще актуальны, перечисляет и объясняет все CreationOptions и это кэширование автоматически в большинстве режимов.

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

Подход

если вы хотите контролировать кэширование самостоятельно и кэшировать HTTPS-ресурсы, то есть несколько хороших примеров...


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

его можно использовать как binding конвертер, поэтому вы даже не должны менять свой код! Просто обновите XAMLs!

пожалуйста, проверьте образцы в хранилище, вам понравится;)

характеристики:

  • кэширование диск
  • кэширование в
  • полностью асинхронный
  • доступно как конвертер привязки или программно из вашего кода
  • полностью открытый исходный код, вилка и улучшить его!

вот пример:

<Image Source="{Binding ImageUrl, Converter={StaticResource MyAppJetImageLoaderConverter}}"/>

Я не думаю, что для этого есть сборка, но вы можете сохранить изображения в IsolatedStorage и использовать конвертер, который проверяет доступность интернета и возвращает url-адрес в интернете или в автономном режиме.

быстрый поиск дал это что может быть именно то ,что вы ищете (он совместим с Windows Phone 7 и не может быть лучшим решением для Windows Phone 8)


мое решение: (Сохранить Изображение из интернета в локальное хранилище и привязать сохраненное изображение к странице)

XAML

<ListView ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
    <DataTemplate>
        <!--Some code removed-->
        <Image Source="{Binding Img_Thumb.Result}" />
    </DataTemplate>
</ListView.ItemTemplate>

DataModel

public class DataModel_ListOfEvents
{
    public DataModel_ListOfEvents(String img_thumb)
    {
        this.Img_Thumb = new NotifyTaskCompletion<string>(JsonCached.ImageFromCache2(img_thumb));
    }
    public NotifyTaskCompletion<string> Img_Thumb { get; private set; }
}

public sealed class SampleData_ListOfEvents
{
    private static SampleData_ListOfEvents _sampleDataSource = new SampleData_ListOfEvents();

    private ObservableCollection<DataModel_ListOfEvents> _items = new ObservableCollection<DataModel_ListOfEvents>();
    public ObservableCollection<DataModel_ListOfEvents> Items { get { return this._items; } }
}

магия

public class JsonCached
{
    public static async Task<string> ImageFromCache2(string path)
    {
        int ru = path.IndexOf(".ru") + 4;// TODO: .com .net .org
        string new_path = path.Substring(ru).Replace("/", "\");

        StorageFolder localFolder = ApplicationData.Current.LocalFolder;
        try
        {
            Stream p = await localFolder.OpenStreamForReadAsync(new_path);
            p.Dispose();
            System.Diagnostics.Debug.WriteLine("From cache");
            return localFolder.Path + "\" + new_path;
        }
        catch (FileNotFoundException)
        {

        }
        catch (Exception e)
        {
            System.Diagnostics.Debug.WriteLine("{0}", e.Message);
        }

        StorageFile storageFile = await localFolder.CreateFileAsync(new_path, CreationCollisionOption.OpenIfExists);

        Uri Website = new Uri(path);
        HttpClient http = new HttpClient();
        // TODO: Check connection. Return message on fail.
        System.Diagnostics.Debug.WriteLine("Downloading started");
        byte[] image_from_web_as_bytes = await http.GetByteArrayAsync(Website);

        MakeFolders(localFolder, path.Substring(ru));

        Stream outputStream = await storageFile.OpenStreamForWriteAsync();
        outputStream.Write(image_from_web_as_bytes, 0, image_from_web_as_bytes.Length);
        outputStream.Position = 0;

        System.Diagnostics.Debug.WriteLine("Write file done {0}", outputStream.Length);

        outputStream.Dispose();
        return localFolder.Path + "\" + new_path;
    }

    private static async void MakeFolders(StorageFolder localFolder, string path)
    {
        //pics/thumbnail/050/197/50197442.jpg
        int slash = path.IndexOf("/");
        if (slash <= 0) // -1 Not found
            return;

        string new_path = path.Substring(0, slash);
        StorageFolder opened_folder = await localFolder.CreateFolderAsync(new_path, CreationCollisionOption.OpenIfExists);
        string very_new_path = path.Remove(0, new_path.Length + 1);
        MakeFolders(opened_folder, very_new_path);
    }
}

NotifyTaskCompletion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

namespace App2.NotifyTask
{
    public sealed class NotifyTaskCompletion<TResult> : INotifyPropertyChanged
    {
        public NotifyTaskCompletion(Task<TResult> task)
        {
            Task = task;
            if (!task.IsCompleted)
            {
                var _ = WatchTaskAsync(task);
            }
        }
        private async Task WatchTaskAsync(Task task)
        {
            try
            {
                await task;
            }
            catch
            {
            }
            var propertyChanged = PropertyChanged;
            if (propertyChanged == null)
                return;
            propertyChanged(this, new PropertyChangedEventArgs("Status"));
            propertyChanged(this, new PropertyChangedEventArgs("IsCompleted"));
            propertyChanged(this, new PropertyChangedEventArgs("IsNotCompleted"));
            if (task.IsCanceled)
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsCanceled"));
            }
            else if (task.IsFaulted)
            {
                propertyChanged(this, new PropertyChangedEventArgs("IsFaulted"));
                propertyChanged(this, new PropertyChangedEventArgs("Exception"));
                propertyChanged(this,
                  new PropertyChangedEventArgs("InnerException"));
                propertyChanged(this, new PropertyChangedEventArgs("ErrorMessage"));
            }
            else
            {
                propertyChanged(this,
                  new PropertyChangedEventArgs("IsSuccessfullyCompleted"));
                propertyChanged(this, new PropertyChangedEventArgs("Result"));
            }
        }
        public Task<TResult> Task { get; private set; }
        public TResult Result { get { return (Task.Status == TaskStatus.RanToCompletion) ? Task.Result : default(TResult); } }
        public TaskStatus Status { get { return Task.Status; } }
        public bool IsCompleted { get { return Task.IsCompleted; } }
        public bool IsNotCompleted { get { return !Task.IsCompleted; } }
        public bool IsSuccessfullyCompleted { get { return Task.Status == TaskStatus.RanToCompletion; } }
        public bool IsCanceled { get { return Task.IsCanceled; } }
        public bool IsFaulted { get { return Task.IsFaulted; } }
        public AggregateException Exception { get { return Task.Exception; } }
        public Exception InnerException { get { return (Exception == null) ? null : Exception.InnerException; } }
        public string ErrorMessage { get { return (InnerException == null) ? null : InnerException.Message; } }
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

вы также можете использовать FFImageLoading (https://github.com/molinch/FFImageLoading/)

особенности

  • Xamarin.iOS (min iOS 7), Xamarin.Android (min Android 4), Xamarin.Формы и Windows (WinRT, UWP) поддержка
  • настраиваемый диск и кэширование памяти
  • дедупликация аналогичных запросов загрузки / загрузки
  • ошибка и загрузка заполнителей поддержка
  • изображения можно автоматически downsampled к указанный размер (меньше использования памяти)
  • поддержка WebP
  • загрузка изображений исчезают в анимации поддержка
  • можно повторить загрузку изображений (RetryCount, RetryDelay)
  • на Android прозрачность отключена по умолчанию (настраивается). Сохраняет 50% памяти
  • преобразования поддержка
    • BlurredTransformation
    • CircleTransformation, RoundedTransformation, CornersTransformation
    • ColorSpaceTransformation, GrayscaleTransformation, SepiaTransformation
    • FlipTransformation
    • поддерживает пользовательские преобразования (собственные реализации Платформы ITransformation)

Это так же просто, как:

<ff:MvxCachedImage Name="image"
    VerticalAlignment="Stretch" 
    HorizontalAlignment="Stretch"
    LoadingPlaceholder="loading.png"
    ErrorPlaceholder="error.png"
    RetryCount="3"
    RetryDelay="250"
    DownsampleHeight="300"
    ImagePath="http://lorempixel.com/output/city-q-c-600-600-5.jpg">
</ff: MvxCachedImage >

примеры проектов здесь:https://github.com/molinch/FFImageLoading/tree/master/samples/