Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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, то есть несколько хороших примеров...

Ответ 2

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

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

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

Особенности:

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

Вот пример:

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

Ответ 3

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

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

Ответ 4

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

Функции

  • Xamarin.iOS(min iOS 7), Xamarin.Android(мин. Android 4), поддержка Xamarin.Forms и Windows (WinRT, UWP).
  • Конфигурируемое кэширование дисков и памяти
  • Дедупликация похожих запросов загрузки/загрузки
  • Поддержка ошибок и загрузки заполнителей
  • Изображения могут автоматически уменьшаться до указанного размера (меньше использования памяти)
  • Поддержка WebP
  • Загрузка изображений Поддержка анимации Fade-In
  • Может повторить загрузку изображений (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/

Ответ 5

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

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;
    }
}