【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(0, 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, 0, 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, 0, 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、自定义可缓存图片控件的实现

【WP8】图片缓存控件
<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(1000);



                        //保存到本地

                        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在读取和写入的时候都加了线程锁(其实不应该在读取文件的时候加锁的),是为了保证,在写入的过程中,读取文件出现无权访问的问题

  暂时没有找到方法支持线程安全,如果你有更好的方案,可以给我留言

 

 

 

  

你可能感兴趣的:(缓存)