原文地址
http://chandermani.blogspot.com/2012/05/caching-images-downloaded-from-web-on.html
目的
应用中用到图片的地方,每次都要网络上拉取,非常耗费流量,效率也不高。
应该将将程序中所有图片缓存到IsolatedStorage。写成一个Converter,可以
应用在所有Image Binding的地方。
策略
1.判断Uri地址是否http或者https的scheme
2.是Http或者Https
2.1本地有缓存优先读取本地的缓存
2.2无网络的情况下:读取默认图像
2.3有网络而且无缓存的时候才到网上获取图像
3.非网络资源直接读取即可
使用方法
1.phone:PhoneApplicationPage.Resources里面定义Converter
<phone:PhoneApplicationPage.Resources><converter:CacheImageFileConverter x:Key="CacheConverter"/>
</phone:PhoneApplicationPage.Resources>
2.引用Converter
xmlns:converter="clr-namespace:ProjectName.Converter"
3.Image控件中绑定
<Image x:Name="ImgBackground"Source="{Binding yourProperty,Converter={StaticResource CacheConverter},ConverterParameter=''}" />
Bug
原文的代码里面
这行代码:
private static IsolatedStorageFile _storage = IsolatedStorageFile.GetUserStoreForApplication();
会引发TypeInitializationException ,导致在Blend里面,使用本Converter的datatemplete无法预览。
原因是:类的静态成员在初始化时如果出现异常,类的其它静态成员或对该类进行初始化都会抛出这个异常。
解决方法:如果类中存在静态成员,应确保其初始化时不会抛出异常,否则会影响对该类的正常
修改如下可以解决:
private static IsolatedStorageFile _storage;public CacheImageFileConverter(){try{if (_storage == null){_storage = IsolatedStorageFile.GetUserStoreForApplication();}}catch (IsolatedStorageException e){Debug.WriteLine(e);}}
完整代码
/// <summary>/// Caches the image that gets downloaded as part of Image control Source property./// </summary>public class CacheImageFileConverter : IValueConverter{private const string ImageStorageFolder = "TempImages";private static IsolatedStorageFile _storage;public CacheImageFileConverter(){try{if (_storage == null){_storage = IsolatedStorageFile.GetUserStoreForApplication();}}catch (IsolatedStorageException e){Debug.WriteLine(e);}}public object Convert(object value, Type targetType, object parameter, CultureInfo culture){var path = value as Uri;if (String.IsNullOrEmpty(path.ToString())) return null;var imageFileUri = path;if (imageFileUri.Scheme == "http" || imageFileUri.Scheme == "https"){// 先看缓存if (_storage.FileExists(GetFileNameInIsolatedStorage(imageFileUri))){return ExtractFromLocalStorage(imageFileUri);}// 再看网络情况if (!DeviceNetworkInformation.IsNetworkAvailable){return LoadDefaultIfPassed(imageFileUri, (parameter ?? string.Empty).ToString());}// 最后再网上获取return DownloadFromWeb(imageFileUri);}// 不是网络图片,应用内的素材var bm = new BitmapImage(imageFileUri);return bm;}public object ConvertBack(object value, Type targetType, object parameter,CultureInfo culture){throw new NotImplementedException();}private static object LoadDefaultIfPassed(Uri imageFileUri, string defaultImagePath){string defaultImageUri = (defaultImagePath ?? string.Empty);if (!string.IsNullOrEmpty(defaultImageUri)){var bm = new BitmapImage(new Uri(defaultImageUri, UriKind.Relative)); //Load default Imagereturn bm;}else{var bm = new BitmapImage(imageFileUri);return bm;}}private static object DownloadFromWeb(Uri imageFileUri){var m_webClient = new WebClient(); //Load from internetvar bm = new BitmapImage();m_webClient.OpenReadCompleted += (o, e) =>{if (e.Error != null || e.Cancelled) return;WriteToIsolatedStorage(IsolatedStorageFile.GetUserStoreForApplication(), e.Result,GetFileNameInIsolatedStorage(imageFileUri));bm.SetSource(e.Result);e.Result.Close();};m_webClient.OpenReadAsync(imageFileUri);return bm;}private static object ExtractFromLocalStorage(Uri imageFileUri){string isolatedStoragePath = GetFileNameInIsolatedStorage(imageFileUri); //Load from local storageusing (IsolatedStorageFileStream sourceFile = _storage.OpenFile(isolatedStoragePath, FileMode.Open,FileAccess.Read)){var bm = new BitmapImage();bm.SetSource(sourceFile);return bm;}}private static void WriteToIsolatedStorage(IsolatedStorageFile storage, Stream inputStream,string fileName){IsolatedStorageFileStream outputStream = null;try{if (!storage.DirectoryExists(ImageStorageFolder)){storage.CreateDirectory(ImageStorageFolder);}if (storage.FileExists(fileName)){storage.DeleteFile(fileName);}outputStream = storage.CreateFile(fileName);var buffer = new byte[32768];int read;while ((read = inputStream.Read(buffer, 0, buffer.Length)) > 0){outputStream.Write(buffer, 0, read);}outputStream.Close();}catch{//We cannot do anything here.if (outputStream != null) outputStream.Close();}}/// <summary>/// Gets the file name in isolated storage for the Uri specified. This name should be used to search in the isolated storage./// </summary>/// <param name="uri">The URI.</param>/// <returns></returns>public static string GetFileNameInIsolatedStorage(Uri uri){return ImageStorageFolder + "\\" + uri.AbsoluteUri.GetHashCode() + ".img";}}