我们正在开发一个WPF桌面应用程序,该应用程序显示当前通过HTTP获取的图像。
图像已针对质量/尺寸进行了优化,但每次获取图像时都会有明显的等待。
有没有办法在客户端缓存图片,以便每次都不会下载?
答案 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接口创建绑定转换器解决了这个问题。鉴于我试图找到一个可靠的解决方案至少一个星期,我想我应该为将来有这个问题的人分享我的解决方案。
答案 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)
基于此,我制作了自定义控件:
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);