原文地址
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 Image
return bm;
}
else
{
var bm = new BitmapImage(imageFileUri);
return bm;
}
}
private static object DownloadFromWeb(Uri imageFileUri)
{
var m_webClient = new WebClient(); //Load from internet
var 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 storage
using (
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";
}
}