在做图片相关的应用的时候,经常需要用大图片的缓存,默认的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

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-08-06
  • 2021-10-27
  • 2022-12-23
  • 2021-09-12
猜你喜欢
  • 2022-12-23
  • 2022-03-09
  • 2021-10-22
  • 2021-07-28
  • 2021-11-27
  • 2021-09-08
  • 2022-12-23
相关资源
相似解决方案