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);
}
}
}
}