如何在WPF应用程序的客户端上缓存图像?

时间:2009-12-10 01:14:25

标签: wpf http image

我们正在开发一个WPF桌面应用程序,该应用程序显示当前通过HTTP获取的图像。

图像已针对质量/尺寸进行了优化,但每次获取图像时都会有明显的等待。

有没有办法在客户端缓存图片,以便每次都不会下载?

7 个答案:

答案 0 :(得分:11)

我知道这个问题已经很老了,但我最近在WPF应用程序中不得不使用缓存,发现在.Net 3.5中有一个更好的选项,通过设置UriCachePolicy来设置BitmapImage,这将使用系统 - 级别缓存:

<Image.Source>
  <BitmapImage UriCachePolicy="Revalidate" 
     UriSource="https://farm3.staticflickr.com/2345/2077570455_03891081db.jpg"/>
</Image.Source>

您甚至可以在app.config中设置该值,以使您的所有应用都使用默认值进行缓存:

<system.net>
  <requestCaching defaultPolicyLevel="CacheIfAvailable"/>
</system.net>

您可以在此处找到RequestCacheLevel值的说明:http://msdn.microsoft.com/en-us/library/system.net.cache.requestcachelevel(v=vs.110).aspx

此功能可识别HTTP / 1.1标头,因此如果您设置了Revalidate,它会使用If-Modified-Since标头以避免每次都下载,但仍会检查图像是否已更改,因此您始终拥有正确的图像。

答案 1 :(得分:7)

对于通过Google访问此处的人,我已打包Simon Hartcher发布的原始实现,由Jeroen van Langen重构(以及Ivan Leonenko的调整以使其可绑定),开源NuGet包。

请在此处查看详细信息 - http://floydpink.github.io/CachedImage/

答案 2 :(得分:3)

我通过使用IValueConverter接口创建绑定转换器解决了这个问题。鉴于我试图找到一个可靠的解决方案至少一个星期,我想我应该为将来有这个问题的人分享我的解决方案。

以下是我的博文:Image Caching for a WPF Desktop Application

答案 3 :(得分:3)

我已经阅读了你的博客,这让我想到了这个(我认为更容易)的概念设置:

正如您将注意到的,我重复使用了您共享的一些代码,因此我将与我分享。

创建一个名为CachedImage的新自定义控件。

public class CachedImage : Image
{
    private string _imageUrl;

    static CachedImage()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CachedImage), new FrameworkPropertyMetadata(typeof(CachedImage)));
    }

    public string ImageUrl
    {
        get
        {
            return _imageUrl;
        }
        set
        {
            if (value != _imageUrl)
            {
                Source = new BitmapImage(new Uri(FileCache.FromUrl(value)));
                _imageUrl = value;
            }
        }
    }
}

接下来我做了一个FileCache类(所以我可以控制所有缓存而不仅仅是图像)

public class FileCache
{
    public static string AppCacheDirectory { get; set; }

    static FileCache()
    {
        // default cache directory, can be changed in de app.xaml.
        AppCacheDirectory = String.Format("{0}/Cache/", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
    }

    public static string FromUrl(string url)
    {
        //Check to see if the directory in AppData has been created 
        if (!Directory.Exists(AppCacheDirectory))
        {
            //Create it 
            Directory.CreateDirectory(AppCacheDirectory);
        }

        //Cast the string into a Uri so we can access the image name without regex 
        var uri = new Uri(url);
        var localFile = String.Format("{0}{1}", AppCacheDirectory, uri.Segments[uri.Segments.Length - 1]);

        if (!File.Exists(localFile))
        {
            HttpHelper.GetAndSaveToFile(url, localFile);
        }

        //The full path of the image on the local computer 
        return localFile;
    }
}

另外,为了下载内容,我做了一个帮助类:

public class HttpHelper
{
    public static byte[] Get(string url)
    {
        WebRequest request = HttpWebRequest.Create(url);
        WebResponse response = request.GetResponse();

        return response.ReadToEnd();
    }

    public static void GetAndSaveToFile(string url, string filename)
    {
        using (FileStream stream = new FileStream(filename, FileMode.Create, FileAccess.Write))
        {
            byte[] data = Get(url);
            stream.Write(data, 0, data.Length);
        }
    }
}

HttpHelper使用WebResponse类的扩展来读取数组的结果

public static class WebResponse_extension
{
    public static byte[] ReadToEnd(this WebResponse webresponse)
    {
        Stream responseStream = webresponse.GetResponseStream();

        using (MemoryStream memoryStream = new MemoryStream((int)webresponse.ContentLength))
        {
            responseStream.CopyTo(memoryStream);
            return memoryStream.ToArray();
        }
    }
}

现在你完成了它,让我们在xaml中使用它

<Grid>
    <local:CachedImage ImageUrl="http://host/image.png" />
</Grid>

这就是全部,它可以重复使用并且非常强大。

唯一的缺点是,在清理缓存目录之前,图像永远不会再次下载。

第一次从网上下载图像并保存在缓存目录中。 最终,图像从缓存加载并分配给父类的源(Image)。

亲切的问候, Jeroen van Langen。

答案 4 :(得分:2)

如果您只是尝试在同一次运行中进行缓存,那么本地字典可以用作运行时缓存。

如果您尝试在应用程序运行之间进行缓存,则会变得更加棘手。

如果这是桌面应用程序,只需将缓存的图像保存在用户的应用程序数据文件夹中。

如果它是XBAP应用程序(浏览器中的WPF),由于安全性,您将只能在用户的Isolated Storage中设置本地缓存。

答案 5 :(得分:1)

基于此,我制作了自定义控件:

  • 可以异步下载图像并从缓存中获取图像
  • 是线程安全的
  • 已下载
  • 具有可绑定到
  • 的依赖项属性
  • 更新图片,在初始Feed中提供新名称(不要忘记保持缓存清理操作,例如,您可以解析您的Feed 并且异步删除Feed中没有链接的图片)

I made a blog post:,这是代码:

public class CachedImage : Image
{
    static CachedImage()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CachedImage), new FrameworkPropertyMetadata(typeof(CachedImage)));
    }

    public readonly static DependencyProperty ImageUrlProperty = DependencyProperty.Register("ImageUrl", typeof(string), typeof(CachedImage), new PropertyMetadata("", ImageUrlPropertyChanged));

    public string ImageUrl
    {
        get
        {
            return (string)GetValue(ImageUrlProperty);
        }
        set
        {
            SetValue(ImageUrlProperty, value);
        }
    }

    private static readonly object SafeCopy = new object();

    private static void ImageUrlPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        var url = (String)e.NewValue;
        if (String.IsNullOrEmpty(url))
            return;

        var uri = new Uri(url);
        var localFile = String.Format(Path.Combine(Globals.CacheFolder, uri.Segments[uri.Segments.Length - 1]));
        var tempFile = String.Format(Path.Combine(Globals.CacheFolder, Guid.NewGuid().ToString()));

        if (File.Exists(localFile))
        {
            SetSource((CachedImage)obj, localFile);
        }
        else
        {
            var webClient = new WebClient();
            webClient.DownloadFileCompleted += (sender, args) =>
                                                    {
                                                        if (args.Error != null)
                                                        {
                                                            File.Delete(tempFile);
                                                            return;
                                                        }
                                                        if (File.Exists(localFile))
                                                            return;
                                                        lock (SafeCopy)
                                                        {
                                                            File.Move(tempFile, localFile);
                                                        }
                                                        SetSource((CachedImage)obj, localFile);
                                                    };

            webClient.DownloadFileAsync(uri, tempFile);
        }
    }

    private static void SetSource(Image inst, String path)
    {
        inst.Source = new BitmapImage(new Uri(path));
    }
}

现在你可以绑定它了:

<Cache:CachedImage ImageUrl="{Binding Icon}"/>

答案 6 :(得分:0)

来自Jeroen van Langen的回复,

你可以保存一堆行

删除HttpHelper课程和WebResponse_extension

替换

HttpHelper.GetAndSaveToFile(url, localFile);

通过

WebClient webClient = new WebClient();
    webClient.DownloadFile(url, localFile);