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

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

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

      

  • 相关阅读:
    MSSQL server 2005 ALTER TABLE
    行业趋势:中国IT产业未来七大赚钱模式
    BLOG之路
    软件企业实施CMM/CMMI面临的五个关键问题
    CSV文件及其使用
    电脑操作最忌讳的18个小动作
    请收听舍得微博
    SuperMemo UX汉化要点
    发音、听力两不误——精简版Tell Me More训练方案
    舍得的十八般武器(常用电脑软件【2012版】)
  • 原文地址:https://www.cnblogs.com/bomo/p/3861306.html
Copyright © 2020-2023  润新知