web-dev-qa-db-ja.com

画像ソースとキャッシング

次のコードを使用して、Webサーバーからの画像を表示します。

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

画像は自動的にダウンロードされ、URLに基​​づいたキャッシュもあると思います。

私の問題は、アプリがオフラインのとき、おそらくキャッシュされた画像が表示されないことです。

利用可能なネットワークがないときに画像も読み込まれるように、キャッシュ動作を変更する方法はありますか?キャッシングに関するドキュメントへのポインタも非常に役立ちます。

13
thumbmunkeys

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リソースをキャッシュしたい場合は、いくつかの良い例があります...

24
Neil Turner

私はあなたのための解決策を持っています。 JetImageLoader です。大量のロゴやアイコンなどを読み込んでキャッシュし、表示する必要があるアプリケーション用に作成しました。

バインディングコンバーターとして使用できるので、コードを変更することすらしないでください! XAMLを更新するだけです!

どうぞ、チェックしてください リポジトリ内のサンプル 、あなたはそれを気に入るはずです;)

機能:

  • ディスクでのキャッシュ
  • メモリ内のキャッシュ
  • 完全非同期
  • バインディングコンバーターとして、またはコードからプログラムで利用可能
  • 完全にオープンソースで、フォークして改善してください!

次に例を示します。

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

それを行う方法はないと思いますが、IsolatedStorageに画像を保存し、インターネットの可用性をチェックしてオンラインまたはオフラインのURLを返すコンバーターを使用することができます。

クイック検索でこれが得られました これはまさにあなたが探しているものかもしれません(Windows Phone 7と互換性があり、Windows Phone 8に最適なソリューションではない可能性があります)

1
Stefan Wexel

FFImageLoadingを使用することもできます( https://github.com/molinch/FFImageLoading/

特徴

  • Xamarin.iOS(最小iOS 7)、Xamarin.Android(最小Android 4)、Xamarin.FormsおよびWindows(WinRT、UWP)のサポート
  • 構成可能なディスクとメモリのキャッシュ
  • 同様のダウンロード/ロード要求の重複排除
  • エラーと読み込みプレースホルダーのサポート
  • 画像は指定されたサイズに自動的にダウンサンプリングできます(メモリ使用量が少なくなります)
  • 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/

1
Daniel Luberda

私の解決策:(画像をWebからローカルストレージに保存し、保存した画像をページにバインドする)

XAML

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

データ・モデル

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