缓存下载任务
using NewTempo.Ftp; using NshowAdClient.Events; using NshowAdClient.Helper; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Xml.Serialization; namespace NshowAdClient.Services { public class CacheFileEventArgs : EventArgs { public bool IsFaulted { get; private set; } public CacheFileModel CacheFile { get; private set; } public CacheFileEventArgs(CacheFileModel cacheFile) { CacheFile = cacheFile; IsFaulted = false; } public CacheFileEventArgs() { IsFaulted = true; } } public class CacheFileModel { public CacheFileModel() { CreateTime = DateTime.Now; LastUseTime = DateTime.Now; } public string RemoteFile { get; set; } public string LocalFile { get; set; } public DateTime CreateTime { get; set; } public DateTime LastUseTime { get; set; } } /// <summary> /// 文件缓存接口 /// </summary> public interface IFileCache { void GetUserFile(string remoteFile, int userId, EventHandler<CacheFileEventArgs> action); void GetShareFile(string remoteFile, EventHandler<CacheFileEventArgs> action); } public class FileCacheMgr : IFileCache { /// <summary> /// 缓存内容的目录 /// </summary> private const string CacheDir = "CacheFile"; /// <summary> /// 缓存数据的文件名 /// </summary> private const string CacheDataFile = "file.cache"; // ReSharper disable once InconsistentNaming private static readonly FileCacheMgr instance = new FileCacheMgr(); public static FileCacheMgr Instance { get { return instance; } } private FileCacheMgr() { Initialize(); } /// <summary> /// 缓存数据文件的读写锁 /// </summary> readonly object _cacheDataFileLock = new object(); /// <summary> /// 管理缓存数据的锁对象 /// </summary> readonly object _cacheLock = new object(); /// <summary> /// 缓存数据任务的锁对象 /// </summary> readonly object _cacheTaskLock = new object(); /// <summary> /// 缓存数据字典 /// Key : url /// Value : CacheModel /// </summary> Dictionary<string, CacheFileModel> _cacheDict = new Dictionary<string, CacheFileModel>(); /// <summary> /// 下载任务字典 /// Key:RemoteUlr /// Value:任务完成时的回调 /// </summary> readonly Dictionary<string, WeakDelegateCollection<CacheFileEventArgs>> _cacheTaskDict = new Dictionary<string, WeakDelegateCollection<CacheFileEventArgs>>(); void Initialize() { LoadCacheData(); } #region CacheDataOperation /// <summary> /// 读取缓存 /// </summary> void LoadCacheData() { lock (_cacheDataFileLock) { try { //缓存数据文件不存在则删除缓存文件夹的内容 if (!File.Exists(CacheDataFile) && Directory.Exists(CacheDir)) Directory.Delete(CacheDir, true); var xs = new XmlSerializer(typeof(List<CacheFileModel>)); using (Stream stream = new FileStream(CacheDataFile, FileMode.Open, FileAccess.Read)) { var list = xs.Deserialize(stream) as List<CacheFileModel> ?? new List<CacheFileModel>(); _cacheDict = list.ToDictionary(m => m.RemoteFile); } } catch (Exception ex) { Logger.Error("读取文件缓存数据失败!", ex); } } } /// <summary> /// 保存 /// </summary> void SaveCacheData() { lock (_cacheDataFileLock) { try { var xs = new XmlSerializer(typeof(List<CacheFileModel>)); using (Stream stream = new FileStream(CacheDataFile, FileMode.Create, FileAccess.Write)) { xs.Serialize(stream, _cacheDict.Values.ToList<CacheFileModel>()); } } catch (Exception ex) { Logger.Error("读取文件缓存数据失败!", ex); File.Delete(CacheDataFile); } } } /// <summary> /// 清除过期的缓存数据 /// </summary> public void CleanExpireCache() { try { List<string> cleanList = new List<string>(); foreach (var item in _cacheDict) { if (DateTime.Now - item.Value.LastUseTime > TimeSpan.FromDays(7)) cleanList.Add(item.Key); } foreach (var item in cleanList) { File.Delete(_cacheDict[item].LocalFile); _cacheDict.Remove(item); } SaveCacheData(); } catch (Exception e) { Logger.Error("清理过期缓存数据失败!", e); } } /// <summary> /// 添加缓存数据 /// </summary> /// <param name="model">数据实体</param> void AddCacheData(CacheFileModel model) { if (model == null) throw new ArgumentNullException("model"); lock (_cacheLock) { if (_cacheDict.ContainsKey(model.RemoteFile) == false) { _cacheDict.Add(model.RemoteFile, model); SaveCacheData(); } } } /// <summary> /// 提出缓存数据 /// </summary> /// <param name="model">数据实体</param> void RemoveCacheData(CacheFileModel model) { if (model == null) throw new ArgumentNullException("model"); lock (_cacheLock) { if (File.Exists(model.LocalFile)) File.Delete(model.LocalFile); if (_cacheDict.ContainsKey(model.RemoteFile)) { _cacheDict.Remove(model.RemoteFile); SaveCacheData(); } } } #endregion CacheDataOperation /// <summary> /// 获取用户的资源文件 /// </summary> /// <param name="remoteFile">远程文件</param> /// <param name="userId">用户Id</param> /// <param name="callback">获取文件的回调(成功或失败)</param> public void GetUserFile(string remoteFile, int userId, EventHandler<CacheFileEventArgs> callback) { GetCacheFile(remoteFile, callback, () => FtpHelpService.GetClientFtp(userId)); } /// <summary> /// 获取共享资源文件 /// </summary> /// <param name="remoteFile">远程文件</param> /// <param name="callback">获取文件的回调</param> public void GetShareFile(string remoteFile, EventHandler<CacheFileEventArgs> callback) { GetCacheFile(remoteFile, callback, FtpHelpService.GetShareFtp); } /// <summary> /// 获取缓存文件 /// 如果缓存不存在则创建下载任务 /// </summary> /// <param name="remoteFile">远程文件</param> /// <param name="callback">获取文件的回调</param> /// <param name="getFtpFunc">获取FTP的委托(不存在缓存文件时,使用该FTP下载文件)</param> void GetCacheFile(string remoteFile, EventHandler<CacheFileEventArgs> callback, Func<MyFtp> getFtpFunc) { if (_cacheDict.ContainsKey(remoteFile)) { CacheFileModel cache = _cacheDict[remoteFile]; if (File.Exists(cache.LocalFile)) { cache.LastUseTime = DateTime.Now; SaveCacheData(); if (callback != null) { callback(this, new CacheFileEventArgs(cache)); } //跳出方法 return; } else { //本地文件不存在 _cacheDict.Remove(remoteFile); } } //添加下载远程文件任务 CreateDownloadTask(remoteFile, getFtpFunc(), callback); } void CreateDownloadTask(string remoteFile, MyFtp myFtp, EventHandler<CacheFileEventArgs> callback) { lock (_cacheTaskLock) { bool exist = _cacheTaskDict.ContainsKey(remoteFile); AddCallbackToDictNoLock(remoteFile, callback); if (exist == false) { Task.Factory.StartNew(() => DownloadFileWork(remoteFile, myFtp, callback), TaskCreationOptions.PreferFairness); } } } void DownloadFileWork(string remoteFile, MyFtp myFtp, EventHandler<CacheFileEventArgs> callback) { string localFile = Path.Combine(CacheDir, Guid.NewGuid().ToString() + Path.GetExtension(remoteFile)); string path = Path.GetDirectoryName(localFile); if (Directory.Exists(path) == false) { Directory.CreateDirectory(path); } var eventArgs = new CacheFileEventArgs(); try { bool dlRet = myFtp.Download(remoteFile, localFile); if (dlRet && File.Exists(localFile)) { var cacheModel = new CacheFileModel() { RemoteFile = remoteFile, LocalFile = localFile, }; eventArgs = new CacheFileEventArgs(cacheModel); //保存缓存信息 AddCacheData(cacheModel); } } finally { try { InvokeCallback(remoteFile, eventArgs); } finally { RemoveCallback(remoteFile); } } } void AddCallbackToDictNoLock(string remoteFile, EventHandler<CacheFileEventArgs> callback) { if (_cacheTaskDict.ContainsKey(remoteFile) == false) _cacheTaskDict.Add(remoteFile, new WeakDelegateCollection<CacheFileEventArgs>()); var weakEvent = _cacheTaskDict[remoteFile]; weakEvent.WeakEvent += callback; } void RemoveCallback(string remoteFile) { lock (_cacheTaskLock) { if (_cacheTaskDict.ContainsKey(remoteFile)) _cacheTaskDict.Remove(remoteFile); } } void InvokeCallback(string remoteFile, CacheFileEventArgs args) { lock (_cacheTaskLock) { if (_cacheTaskDict.ContainsKey(remoteFile) == false) return; _cacheTaskDict[remoteFile].Invoke(this, args); } } } }