【WP8】图片缓存控件
在做图片相关的应用的时候,经常需要用大图片的缓存,默认的Image控件不支持缓存的支持,本文自定义一个支持图片缓存的控件
当图片的地址是网络图片时候
根据Url判断该图片是否存在本地,如果存在,则直接从本地读取,如果不存在,则通过Http请求下载该图片,保存到本地,然后读取到Image控件中
当图片为本地地址的时候,直接从本地读取,设置到Image控件中
1、在定义可缓存图片控件之前,先封装一下文件存储的帮助类
using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Windows.ApplicationModel;
using Windows.Storage;
using Newtonsoft.Json;
using XTuOne.Common.Helpers; namespace XTuOne.Utility.Helpers
{
public class StorageHelper : IStorageHelper
{
#region 单例 public static IStorageHelper Instance { get; private set; } public static object LockObject; static StorageHelper()
{
Instance = new StorageHelper();
LockObject = new object();
} private StorageHelper()
{
} #endregion #region 同步读写方法 public Stream ReadFile(string filePath)
{
lock (LockObject)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!sf.FileExists(filePath))
{
throw new FileNotFoundException(string.Format("没有找到文件:{0}", filePath));
} using (var fs = sf.OpenFile(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var stream = new MemoryStream();
fs.CopyTo(stream); stream.Seek(, SeekOrigin.Begin);
return stream;
}
}
}
} public string CreateFile(Stream stream, string filePath, bool replace = false)
{
lock (LockObject)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
var directory = Path.GetDirectoryName(filePath);
if (directory != null && !sf.DirectoryExists(directory))
{
//如果目录不存在,则创建
sf.CreateDirectory(directory);
} if (FileExist(filePath))
{
if (!replace)
{
return filePath;
}
sf.DeleteFile(filePath);
}
//如果不存在或者存在且替换
using (var fs = sf.CreateFile(filePath))
{
stream.CopyTo(fs);
}
}
}
return filePath;
} public string CreateFile(byte[] data, string filePath, bool replace = false)
{
lock (LockObject)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
var directory = Path.GetDirectoryName(filePath);
if (directory != null && !sf.DirectoryExists(directory))
{
//如果目录不存在,则创建
sf.CreateDirectory(directory);
} if (FileExist(filePath))
{
if (!replace)
{
return filePath;
}
sf.DeleteFile(filePath);
}
//如果不存在或者存在且替换
using (var fs = new IsolatedStorageFileStream(filePath, FileMode.OpenOrCreate, sf))
{
fs.Write(data, , data.Length);
}
}
}
return filePath;
} public string ReadAllText(string fileName)
{
using (var reader = new StreamReader(ReadFile(fileName)))
{
return reader.ReadToEnd();
}
} public string WriteAllText(string fileName, string text, bool replace)
{
return CreateFile(Encoding.UTF8.GetBytes(text), fileName, replace);
} #endregion #region 异步读写方法 public async Task<Stream> ReadFileAsync(string filePath)
{
var storageFile = await GetStorageFileAsync(filePath);
return await storageFile.OpenStreamForReadAsync();
} public async Task<string> CreateFileAsync(Stream stream, string filePath, bool replace = false)
{
var storageFile = await GetStorageFileAsync(filePath);
if (storageFile != null)
{
if (FileExist(filePath))
{
if (replace)
{
//替换先删除
await storageFile.DeleteAsync(StorageDeleteOption.PermanentDelete);
}
else
{
return filePath;
}
} storageFile = await GetStorageFileAsync(filePath);
var destStream = await storageFile.OpenStreamForWriteAsync();
await stream.CopyToAsync(destStream);
}
return filePath;
} public async Task<string> CreateFileAsync(byte[] data, string filePath, bool replace = false)
{
var storageFile = await GetStorageFileAsync(filePath);
if (storageFile != null)
{
if (FileExist(filePath))
{
if (replace)
{
//替换先删除
await storageFile.DeleteAsync(StorageDeleteOption.PermanentDelete);
}
else
{
return filePath;
}
} storageFile = await GetStorageFileAsync(filePath);
var destStream = await storageFile.OpenStreamForWriteAsync();
await destStream.WriteAsync(data, , data.Length);
}
return filePath;
} public async Task<string> ReadAllTextAsync(string fileName)
{
using (var reader = new StreamReader(await ReadFileAsync(fileName)))
{
return await reader.ReadToEndAsync();
}
} public async Task<string> WriteAllTextAsync(string fileName, string text, bool replace)
{
return await CreateFileAsync(Encoding.UTF8.GetBytes(text), fileName, replace);
} #endregion #region 普通方法:判断文件(文件夹)存在,创建(删除)文件夹,获取文件(文件夹) public bool FileExist(string fileName)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
return sf.FileExists(fileName);
}
} public bool DirectoryExist(string directory)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
return sf.DirectoryExists(directory);
}
} public void DeleteFile(string fileName)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
if (sf.FileExists(fileName))
{
sf.DeleteFile(fileName);
}
}
} public void CreateDirectory(string directory)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
if (sf.DirectoryExists(directory))
{
sf.DeleteDirectory(directory);
}
} } public void DeleteDirectory(string directory, bool isDeleteAll)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
if (sf.DirectoryExists(directory))
{
if (isDeleteAll)
{
var files = GetFiles(directory);
foreach (var file in files)
{
DeleteFile(file);
} var directories = GetDirectories(directory);
foreach (var s in directories)
{
DeleteDirectory(s, true);
}
}
sf.DeleteDirectory(directory);
}
}
} public string[] GetFiles(string directory)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
return sf.GetFileNames(directory);
}
} /// <summary>
/// 获取本地文件夹中的文件
/// </summary>
public string[] GetDirectories(string directory)
{
using (var sf = IsolatedStorageFile.GetUserStoreForApplication())
{
return sf.GetDirectoryNames(directory);
}
} #endregion #region 拷贝文件(从安装包到本地) /// <summary>
/// 从安装包拷贝文件到本地
/// </summary>
public async Task CopyPackageFileToLocalAsync(string source, string target = null, bool replace = false)
{
using (var stream = GetResourceStream(source))
{
await CreateFileAsync(stream, target ?? source, replace);
}
} /// <summary>
/// 从安装包拷贝路径到本地
/// </summary>
public async Task CopyPackageFolderToLocalAsync(string source, string target = null, bool replace = false)
{
target = target ?? source; var packagePath = Package.Current.InstalledLocation;
var folder = await GetStorageFolderAsync(packagePath, source); //拷贝文件
var files = await folder.GetFilesAsync();
foreach (var storageFile in files)
{
var fileName = storageFile.Name;
using (var stream = await storageFile.OpenStreamForReadAsync())
{
await CreateFileAsync(stream, target + fileName, replace);
}
} //拷贝子文件夹(递归)
var folders = await folder.GetFoldersAsync();
foreach (var storageFolder in folders)
{
await
CopyPackageFolderToLocalAsync(source + storageFolder.Name + "/", target + storageFolder.Name + "/",
replace);
}
} #endregion #region 从安装包(安装路径)中读取(同步) public Stream GetResourceStream(string file)
{
//引用安装路径的文件的时候不以'/'开头
file = file.TrimStart('/');
return Application.GetResourceStream(new Uri(file, UriKind.Relative)).Stream;
} #endregion #region 序列化 public void Serialize<T>(string fileName, T obj, bool replace)
{
var json = JsonConvert.SerializeObject(obj);
WriteAllText(fileName, json, replace);
} T IStorageHelper.DeSerialize<T>(string fileName)
{
var json = ReadAllText(fileName);
return JsonConvert.DeserializeObject<T>(json);
} public async Task SerializeAsync<T>(string fileName, T obj, bool replace)
{
var json = JsonConvert.SerializeObject(obj);
await WriteAllTextAsync(fileName, json, replace);
} public async Task<T> DeSerializeAsync<T>(string fileName)
{
var json = await ReadAllTextAsync(fileName);
return JsonConvert.DeserializeObject<T>(json);
} #endregion #region 辅助方法 /// <summary>
/// 根据路劲获取StorageFolder
/// </summary>
private async Task<StorageFolder> GetStorageFolderAsync(StorageFolder folder, string directory)
{
var directories = directory.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); foreach (var s in directories)
{
folder = await folder.CreateFolderAsync(s, CreationCollisionOption.OpenIfExists);
}
return folder;
} /// <summary>
/// 根据文件名异步获取本地文件夹StorageFolder(如果路径不存在,则创建路径)
/// </summary>
private async static Task<StorageFolder> GetStorageFolderAsync(string filePath)
{
var localFolder = ApplicationData.Current.LocalFolder;
var directory = Path.GetDirectoryName(filePath); if (!string.IsNullOrEmpty(directory))
{
var directories = directory.Split(new[] {'\\', '/'}, StringSplitOptions.RemoveEmptyEntries); foreach (var s in directories)
{
localFolder = await localFolder.CreateFolderAsync(s, CreationCollisionOption.OpenIfExists);
}
}
return localFolder;
} /// <summary>
/// 根据路径得到StoreageFile
/// </summary>
private async static Task<StorageFile> GetStorageFileAsync(string filePath)
{
var folder = await GetStorageFolderAsync(filePath);
var fileName = Path.GetFileName(filePath);
if (fileName != null)
{
return await folder.CreateFileAsync(fileName, CreationCollisionOption.OpenIfExists);
}
return null;
} #endregion
}
}
图片的写入和读取都使用了线程锁,在最后说明
注意:上面的异步方法是线程不安全的,在多线程的情况下,当文件被一个线程写入的时候,另一个线程调用读的方法会抛出异常 Access Deny,访问被阻止
实现了StorageHelper,下面是CacheableImage的实现,支持占位图片,加载失败图片,配置保存路径
2、自定义可缓存图片控件的实现
<UserControl x:Class="XTuOne.Controls.CacheableImage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480"> <Grid x:Name="LayoutRoot">
<Image x:Name="Image" Stretch="Fill"></Image>
</Grid>
</UserControl>
CacheableImage.xaml
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using XTuOne.Utility.Helpers; namespace XTuOne.Controls
{
/// <summary>
/// 支持本地缓存的图片空间
/// </summary>
public partial class CacheableImage
{
public CacheableImage()
{
InitializeComponent();
} public static readonly DependencyProperty CachedDirectoryProperty = DependencyProperty.Register(
"CachedDirectory", typeof (string), typeof (CacheableImage), new PropertyMetadata("/ImageCached/")); public static readonly DependencyProperty FaildImageUrlProperty = DependencyProperty.Register(
"FaildImageUrl", typeof(Uri), typeof(CacheableImage), new PropertyMetadata(default(string))); public static readonly DependencyProperty LoadingImageUrlProperty = DependencyProperty.Register(
"LoadingImageUrl", typeof(Uri), typeof(CacheableImage), new PropertyMetadata(default(string))); public static readonly DependencyProperty StretchProperty = DependencyProperty.Register(
"Stretch", typeof (Stretch), typeof (CacheableImage), new PropertyMetadata(default(Stretch), StretchPropertyChangedCallback)); private static void StretchPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var cachedImage = (CacheableImage)dependencyObject;
var stretch = (Stretch)dependencyPropertyChangedEventArgs.NewValue;
if (cachedImage.Image != null)
{
cachedImage.Image.Stretch = stretch;
}
} public Stretch Stretch
{
get { return (Stretch) GetValue(StretchProperty); }
set { SetValue(StretchProperty, value); }
} /// <summary>
/// 加载失败的图片
/// </summary>
public Uri FaildImageUrl
{
get { return (Uri)GetValue(FaildImageUrlProperty); }
set { SetValue(FaildImageUrlProperty, value); }
} /// <summary>
/// 加载中显示的图片(需要进行网络请求时)
/// </summary>
public Uri LoadingImageUrl
{
get { return (Uri)GetValue(LoadingImageUrlProperty); }
set { SetValue(LoadingImageUrlProperty, value); }
} /// <summary>
/// 缓存到本地的目录
/// </summary>
public string CachedDirectory
{
get { return (string) GetValue(CachedDirectoryProperty); }
set { SetValue(CachedDirectoryProperty, value); }
} public static readonly DependencyProperty ImageUrlProperty = DependencyProperty.Register(
"ImageUrl", typeof (string), typeof (CacheableImage), new PropertyMetadata(default(string), ImageUrlPropertyChangedCallback)); public string ImageUrl
{
get { return (string)GetValue(ImageUrlProperty); }
set { SetValue(ImageUrlProperty, value); }
} private static async void ImageUrlPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var cachedImage = (CacheableImage) dependencyObject;
var imageUrl = (string)dependencyPropertyChangedEventArgs.NewValue; if (string.IsNullOrEmpty(imageUrl))
{
return;
} if (imageUrl.StartsWith("http://") || imageUrl.Equals("https://"))
{
var fileName = cachedImage.CachedDirectory + Uri.EscapeDataString(imageUrl);
//网络图片,判断是否存在
if (!StorageHelper.Instance.FileExist(fileName))
{
try
{
if (cachedImage.LoadingImageUrl != null)
{
cachedImage.Image.Source =
new BitmapImage(cachedImage.LoadingImageUrl);
} //请求
var request = WebRequest.CreateHttp(imageUrl);
request.AllowReadStreamBuffering = true;
var response = await request.GetResponseAsync();
var stream = response.GetResponseStream();
await Task.Delay(); //保存到本地
StorageHelper.Instance.CreateFile(stream, fileName);
}
catch (Exception e)
{
//请求失败
if (cachedImage.FaildImageUrl != null)
{
cachedImage.Image.Source = new BitmapImage(cachedImage.FaildImageUrl);
}
return;
}
}
//读取图片文件
var imageStream = StorageHelper.Instance.ReadFile(fileName);
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
cachedImage.Image.Source = bitmapImage;
}
else
{
//本地图片
var bitmapImage = new BitmapImage(new Uri(imageUrl, UriKind.Relative));
cachedImage.Image.Source = bitmapImage;
}
} public static readonly DependencyProperty ImageStreamProperty = DependencyProperty.Register(
"ImageStream", typeof (Stream), typeof (CacheableImage), new PropertyMetadata(default(Stream), ImageStreamPropertyChangedCallback)); private static void ImageStreamPropertyChangedCallback(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var cachedImage = (CacheableImage) dependencyObject;
var imageStream = (Stream) dependencyPropertyChangedEventArgs.NewValue; var bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
cachedImage.Image.Source = bitmapImage;
} /// <summary>
/// 支持直接传递流进来
/// </summary>
public Stream ImageStream
{
get { return (Stream) GetValue(ImageStreamProperty); }
set { SetValue(ImageStreamProperty, value); }
}
}
}
为了保证线程安全,这里图片的保存没有用到异步,因为如果有很多图片进行请求的时候,可能会线程请求异常,像上面说的情况
上面的StorageHelper在读取和写入的时候都加了线程锁(其实不应该在读取文件的时候加锁的),是为了保证,在写入的过程中,读取文件出现无权访问的问题
暂时没有找到方法支持线程安全,如果你有更好的方案,可以给我留言
【WP8】图片缓存控件的更多相关文章
- 【WPF】【UWP】借鉴 asp.net core 管道处理模型打造图片缓存控件 ImageEx
在 Web 开发中,img 标签用来呈现图片,而且一般来说,浏览器是会对这些图片进行缓存的. 比如访问百度,我们可以发现,图片.脚本这种都是从缓存(内存缓存/磁盘缓存)中加载的,而不是再去访问一次百度 ...
- UI-UIImageView的图片填充方式(contentMode)_图片作为控件背景图的拉伸方式(stretch)介绍
常用图片填充方式 这里只介绍三个最常用的图片填充方式 UIViewContentModeScaleToFill模式会导致图片变形.例如: UIViewContentModeScaleAspectFit ...
- 我写的一个 Qt 显示图片的控件
Qt 中没有专门显示图片的控件.通常我们会使用QLabel来显示图片.可是QLabel 显示图片的能力还是有点弱.比方不支持图像的缩放一类的功能.使用起来不是非常方便. 因此我就自己写了个简单的类. ...
- Android开发技巧——定制仿微信图片裁剪控件
拍照--裁剪,或者是选择图片--裁剪,是我们设置头像或上传图片时经常需要的一组操作.上篇讲了Camera的使用,这篇讲一下我对图片裁剪的实现. 背景 下面的需求都来自产品. 裁剪图片要像微信那样,拖动 ...
- MFC入门(三)-- MFC图片/文字控件(循环显示文字和图片的小程序)
惯例附上前几个博客的链接: MFC入门(一)简单配置:http://blog.csdn.net/zmdsjtu/article/details/52311107 MFC入门(二)读取输入字符:http ...
- 关于IOS某图片添加控件,图片从相册或拍照保存后,再次进入时点击放大图无法显示的问题
某图片添加控件: https://github.com/XZTLLQ/LQPhotoPickerDemo 问题: 标题已说明 代码块: NSArray *alAssetUrl =(NSMutableA ...
- 图片剪裁控件——ClipImageView
这段时间在做自己的项目时,须要使用到图片剪裁功能,当时大概的思考了一些需求.想到了比較简单的实现方法.因此就抽了点时间做了这个图片剪裁控件--ClipImageView 这里先贴上ClipImageV ...
- Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来
首先题外话,今天早上起床的时候,手滑一下把我的手机甩了出去,结果陪伴我两年半的摩托罗拉里程碑一代就这么安息了,于是我今天决定怒更一记,纪念我死去的爱机. 如果你是网购达人,你的手机上一定少不了淘宝客户 ...
- 用MVVM模式开发中遇到的零散问题总结(5)——将动态加载的可视元素保存为图片的控件,Binding刷新的时机
原文:用MVVM模式开发中遇到的零散问题总结(5)--将动态加载的可视元素保存为图片的控件,Binding刷新的时机 在项目开发中经常会遇到这样一种情况,就是需要将用户填写的信息排版到一张表单中,供打 ...
随机推荐
- Yum Priorities
简介 Linux 发行版比较多,同时还有很多个人或组织维护了某些特定用途的安装/升级源.Yum Priorities 插件可以用来强制保护源.它通过给各个源设定不同的优先级,使得系统管理员可以将某些源 ...
- JAVA-JSP内置对象之移除属性
相关资料:<21天学通Java Web开发> 移除属性1.只需调用相应对象的removeAttribute()方法就可以移除指定属性 RemoveAttributeDemo.jsp < ...
- 【转】oracle CONNECT BY PRIOR叶子节点查找根节点
SELECT TRANS_ID FROM TRANS_INST WHERE connect_by_isleaf=1 START WITH TRANS_ID =480242 CONNECT BY PRI ...
- VS2013代码调试:[7]如何避免调试时加载符号
调试的时候不小心点了个东西,然后就花很长时间加在各种dll 解决办法: 1.点 调试2.然后 选项和设置3.右边勾上 启用源服务器支持4.左边点 符号5.把微软符号服务器勾6.运行的时候等一下 莫慌! ...
- flume中HdfsSink参数说明
flume到hdfsSink: type hdfs path 写入hdfs的路径,需要包含文件系统标识,比如:hdfs://namenode/flume/webdata/ 可以使用flume提供的日期 ...
- shell将字符串分隔成数组
#!/bin/bash a="hello,world,nice,to,meet,you" #要将$a分割开,先存储旧的分隔符 OLD_IFS="$IFS" #设 ...
- Web服务端开发需要考虑的问题
API设计 是否Restful. 首先需要清楚,Restful是一种风格而不是规范,不存在必须遵守的问题. Restful本质上是对HTTP API进行有效的分类. 分类是应该的,可以让API组织变得 ...
- 【转】工作中使用Trepn Power Profiler的应用总结
Trepn™ Profiler 工具的概述 Trepn™工具是高通开发的运行在使用高通骁龙芯片或者硬件开发设备等移动设备上 分析功耗和性能的一个应用. ## 特点 ## 1 2 3 Six fast- ...
- Python 类的初见
#定义一个Python类 class Cat: #self关键字相当于c++类中的this指针 def eat(self): print("i am eating .") def ...
- GRAPH ATTENTION NETWORKS
基本就是第一层concatenate,第二层不concatenate. 相关论文: Semi-Supervised Classification with Graph Convolutional Ne ...