天津23维预案
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

738 lines
24 KiB

using System;
using System.Collections.Generic;
using System.IO;
using AX.Network.Common;
using AX.Network.Protocols;
using IAppSession = AX.Network.Common.IAppSession<AX.Network.Protocols.BinaryProtocol, AX.Network.Protocols.BinaryMessage>;
namespace AX.NetworkSystem
{
/// <summary>
/// 表示传输类型。
/// </summary>
public enum TransferType
{
/// <summary>
/// 下载。
/// </summary>
Download,
/// <summary>
/// 上传。
/// </summary>
Upload
}
/// <summary>
/// 表示文件块信息。
/// </summary>
public class ChunkInfo
{
/// <summary>
/// 表示文件块对应的文件。
/// </summary>
public string Filename;
/// <summary>
/// 表示文件的大小。
/// </summary>
public int FileSize;
/// <summary>
/// 表示文件块的 ID。
/// </summary>
public int ChunkID;
/// <summary>
/// 表示文件块的总数。
/// </summary>
public int ChunkCount;
/// <summary>
/// 表示文件块划分的标准大小。
/// </summary>
public int ChunkSize;
public ChunkInfo() { }
public ChunkInfo(string filename, int fileSize, int chunkID, int chunkCount, int chunkSize)
{
this.Filename = filename;
this.FileSize = fileSize;
this.ChunkID = chunkID;
this.ChunkCount = chunkCount;
this.ChunkSize = chunkSize;
}
}
/// <summary>
/// 表示简单的文件块信息。
/// </summary>
public struct SimpleChunkInfo
{
/// <summary>
/// 表示文件块对应的文件。
/// </summary>
public string Filename;
/// <summary>
/// 表示文件块的 ID。
/// </summary>
public int ChunkID;
public SimpleChunkInfo(string filename, int chunkID)
{
this.Filename = filename;
this.ChunkID = chunkID;
}
}
/// <summary>
/// 表示有附加数据的文件块信息。
/// </summary>
public struct AttachedChunkInfo
{
/// <summary>
/// 表示文件块对应的文件。
/// </summary>
public string Filename;
/// <summary>
/// 表示文件块的 ID。
/// </summary>
public int ChunkID;
/// <summary>
/// 表示文件块的总数。
/// </summary>
public int ChunkCount;
/// <summary>
/// 表示文件块划分的标准。
/// </summary>
public int ChunkSize;
/// <summary>
/// 表示文件块的数据。
/// </summary>
public ArraySegment<byte> Data;
public AttachedChunkInfo(string filename, int chunkID, int chunkCount, int chunkSize, ArraySegment<byte> data)
{
this.Filename = filename;
this.ChunkID = chunkID;
this.ChunkCount = chunkCount;
this.ChunkSize = chunkSize;
this.Data = data;
}
}
/// <summary>
/// 表示一个文件传输任务。
/// </summary>
public class TransferTask : IDisposable
{
private static readonly BufferPool BufferPool = new BufferPool(1, ChunkSize);
private const string DOWNLOAD_FILE_INFO_REQUEST = "DOWNLOAD_FILE_INFO_REQUEST";
private const string DOWNLOAD_CHUNK_INFO_REQUEST = "DOWNLOAD_CHUNK_INFO_REQUEST";
private const string UPLOAD_FILE_INFO_REQUEST = "UPLOAD_FILE_INFO_REQUEST";
private const string UPLOAD_CHUNK_INFO_REQUEST = "UPLOAD_CHUNK_INFO_REQUEST";
private const int ChunkSize = 64 * 1024;
private IAppSession appSession;
private FileStream filestream;
/// <summary>
/// 表示该任务的 ID。
/// </summary>
public int ID { get; internal set; }
/// <summary>
/// 表示本地的存储文件路径。
/// </summary>
public string[] StoragePath { get; private set; }
/// <summary>
/// 表示该传输任务的传输文件路径。
/// </summary>
public string[] TransferPath { get; private set; }
/// <summary>
/// 表示当前正在传输的文件所对应的索引位置。
/// </summary>
public int Position { get; private set; }
/// <summary>
/// 表示当前正在传输的文件块。
/// </summary>
public ChunkInfo CurrentChunk { get; private set; }
/// <summary>
/// 表示当前传输任务是下载还是上传。
/// </summary>
public TransferType TransferType { get; private set; }
/// <summary>
/// 表示该任务是否已暂停。
/// </summary>
public bool IsPaused { get; private set; }
private bool isStopped;
internal TransferTask(IAppSession session, string[] storagePath, string[] transferPath, TransferType type, int taskID)
{
if (session == null)
throw new ArgumentNullException("session");
this.Reset(storagePath, transferPath, type, taskID);
this.appSession = session;
}
public event Action<string> Started;
public event Action<string> Paused;
public event Action<string> Resumed;
public event Action Stopped;
public event Action<string> TransferFileCompleted;
public event Action TransferTaskCompleted;
public event Action<float, float> ProgressChanged;
protected virtual void OnStarted(string filename)
{
if (Started != null)
Started(filename);
}
protected virtual void OnPaused(string filename)
{
if (Paused != null)
Paused(filename);
}
protected virtual void OnResumed(string filename)
{
if (Resumed != null)
Resumed(filename);
}
protected virtual void OnStopped()
{
if (Stopped != null)
Stopped();
}
protected virtual void OnTransferFileCompleted(string filename)
{
if (TransferFileCompleted != null)
TransferFileCompleted(filename);
}
protected virtual void OnTransferTaskCompleted()
{
if (TransferTaskCompleted != null)
TransferTaskCompleted();
}
protected virtual void OnProgressChanged(float curr, float total)
{
if (ProgressChanged != null)
ProgressChanged(curr, total);
}
public void Start()
{
isStopped = false;
OnStarted(CurrentChunk.Filename);
// 下载
if (TransferType == TransferType.Download)
{
if (CurrentChunk.ChunkID == 0 && CurrentChunk.ChunkCount == 0 && CurrentChunk.FileSize == 0)
appSession.SendRequestAsync(DOWNLOAD_FILE_INFO_REQUEST, CurrentChunk.Filename);
else
{
var info = new SimpleChunkInfo(CurrentChunk.Filename, CurrentChunk.ChunkID);
appSession.SendRequestAsync(DOWNLOAD_CHUNK_INFO_REQUEST, info);
}
}
// 上传
else
{
var filename = StoragePath[Position];
var fileinfo = new FileInfo(filename);
var fileSize = (int)fileinfo.Length;
var chunkCount = fileSize % ChunkSize == 0 ? fileSize / ChunkSize : fileSize / ChunkSize + 1;
CurrentChunk.FileSize = fileSize;
CurrentChunk.ChunkCount = chunkCount;
if (CurrentChunk.ChunkID == 0)
{
var info = new ChunkInfo(CurrentChunk.Filename, fileSize, CurrentChunk.ChunkID, CurrentChunk.ChunkCount, CurrentChunk.ChunkSize);
appSession.SendRequestAsync(UPLOAD_FILE_INFO_REQUEST, info);
}
else
UploadFileChunk(filename);
}
}
public void Pause()
{
if (!isStopped && !IsPaused)
{
IsPaused = true;
OnPaused(CurrentChunk.Filename);
}
}
public void Resume()
{
if (!isStopped && IsPaused)
{
IsPaused = false;
OnResumed(CurrentChunk.Filename);
if (TransferType == TransferType.Download)
{
if (CurrentChunk.ChunkID == 0 && CurrentChunk.ChunkCount == 0 && CurrentChunk.FileSize == 0)
appSession.SendRequestAsync(DOWNLOAD_FILE_INFO_REQUEST, CurrentChunk.Filename);
else
{
var info = new SimpleChunkInfo(CurrentChunk.Filename, CurrentChunk.ChunkID);
appSession.SendRequestAsync(DOWNLOAD_CHUNK_INFO_REQUEST, info);
}
}
else
{
var filename = StoragePath[Position];
var fileinfo = new FileInfo(filename);
var fileSize = (int)fileinfo.Length;
var chunkCount = fileSize % ChunkSize == 0 ? fileSize / ChunkSize : fileSize / ChunkSize + 1;
CurrentChunk.FileSize = fileSize;
CurrentChunk.ChunkCount = chunkCount;
if (CurrentChunk.ChunkID == 0)
{
var info = new ChunkInfo(CurrentChunk.Filename, fileSize, CurrentChunk.ChunkID, CurrentChunk.ChunkCount, CurrentChunk.ChunkSize);
appSession.SendRequestAsync(UPLOAD_FILE_INFO_REQUEST, info);
}
else
UploadFileChunk(filename);
}
}
}
public void Stop()
{
isStopped = true;
IsPaused = false;
OnStopped();
}
public void Dispose()
{
this.Reset();
if (filestream != null)
filestream.Dispose();
if (appSession != null)
appSession = null;
}
public void Reset()
{
this.Position = 0;
this.IsPaused = false;
this.isStopped = false;
this.CurrentChunk = new ChunkInfo(); ;
this.CurrentChunk.Filename = ID + "://" + TransferPath[0];
this.CurrentChunk.FileSize = 0;
this.CurrentChunk.ChunkID = 0;
this.CurrentChunk.ChunkCount = 0;
this.CurrentChunk.ChunkSize = ChunkSize;
}
public void Reset(string[] storagePath, string[] transferPath, TransferType type, int taskID)
{
if (storagePath == null)
throw new ArgumentNullException("storagePath");
if (transferPath == null)
throw new ArgumentNullException("transferPath");
if (storagePath.Length != transferPath.Length)
throw new ArgumentOutOfRangeException("storagePath or transferPath");
for (var i = 0; i < storagePath.Length; ++i)
if (string.IsNullOrEmpty(storagePath[i]))
throw new ArgumentException("storagePath or transferPath");
this.StoragePath = storagePath;
this.TransferPath = transferPath;
this.TransferType = type;
this.ID = taskID;
this.Reset();
}
internal void ProcessDownloadFileReplyInfo(ref ChunkInfo info)
{
CurrentChunk.FileSize = info.FileSize;
CurrentChunk.ChunkID = 0;
CurrentChunk.ChunkCount = info.ChunkCount;
// 先创建文件夹
var filename = StoragePath[Position];
var folder = Path.GetDirectoryName(filename);
if (!string.IsNullOrEmpty(folder) && !Directory.Exists(folder))
Directory.CreateDirectory(folder);
if (isStopped || IsPaused)
return;
var chunkInfo = new SimpleChunkInfo(CurrentChunk.Filename, CurrentChunk.ChunkID);
appSession.SendRequestAsync(DOWNLOAD_CHUNK_INFO_REQUEST, chunkInfo);
}
internal void ProcessDownloadChunkReplyInfo(ref AttachedChunkInfo info)
{
var filename = StoragePath[Position];
if (filestream == null)
filestream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write, info.Data.Count, false);
// 写入文件
filestream.Seek(info.ChunkID * info.ChunkSize, SeekOrigin.Begin);
filestream.Write(info.Data.Array, info.Data.Offset, info.Data.Count);
filestream.Flush();
CurrentChunk.ChunkID += 1;
// 还未下载完该文件的所有文件块
if (CurrentChunk.ChunkID != CurrentChunk.ChunkCount)
{
var currProgress = ((float)info.ChunkID / (float)info.ChunkCount) * 100.0f;
var totalProgress = ((float)Position / (float)StoragePath.Length) * 100.0f;
// 报告进度
OnProgressChanged(currProgress, totalProgress);
if (isStopped || IsPaused)
return;
// 继续下载剩余文件块
var chunkInfo = new SimpleChunkInfo(CurrentChunk.Filename, CurrentChunk.ChunkID);
appSession.SendRequestAsync(DOWNLOAD_CHUNK_INFO_REQUEST, chunkInfo);
}
// 已下载完一个文件的所有文件块
else
{
if (filestream != null)
{
filestream.Close();
filestream = null;
}
++Position;
// 全部下载完成
if (Position == StoragePath.Length)
{
var currProgress = 100.0f;
var totalProgress = 100.0f;
// 报告进度
OnProgressChanged(currProgress, totalProgress);
OnTransferTaskCompleted();
}
else
{
var currProgress = 100.0f;
var totalProgress = ((float)Position / (float)StoragePath.Length) * 100.0f;
// 报告进度
OnProgressChanged(currProgress, totalProgress);
OnTransferFileCompleted(info.Filename);
CurrentChunk.Filename = ID + "://" + TransferPath[Position];
CurrentChunk.ChunkID = 0;
CurrentChunk.ChunkCount = 0;
CurrentChunk.FileSize = 0;
if (isStopped || IsPaused)
return;
// 继续下载剩余的文件
appSession.SendRequestAsync(DOWNLOAD_FILE_INFO_REQUEST, CurrentChunk.Filename);
}
}
}
internal void ProcessUploadFileReplyInfo(ref SimpleChunkInfo info)
{
CurrentChunk.ChunkID = 0;
if (isStopped || IsPaused)
return;
// 开始上传文件块
UploadFileChunk(StoragePath[Position]);
}
internal void ProcessUploadChunkReplyInfo(ref SimpleChunkInfo info)
{
CurrentChunk.ChunkID += 1;
// 表示还没上传完一个文件
if (CurrentChunk.ChunkID != CurrentChunk.ChunkCount)
{
var currProgress = ((float)CurrentChunk.ChunkID / (float)CurrentChunk.ChunkCount) * 100.0f;
var totalProgress = ((float)Position / (float)StoragePath.Length) * 100.0f;
// 报告进度
OnProgressChanged(currProgress, totalProgress);
if (isStopped || IsPaused)
return;
// 继续上传剩余的文件块
UploadFileChunk(StoragePath[Position]);
}
// 已上传完一个文件的所有文件块
else
{
if (filestream != null)
{
filestream.Close();
filestream = null;
}
++Position;
// 全部上传完成
if (Position == StoragePath.Length)
{
var currProgress = 100.0f;
var totalProgress = 100.0f;
// 报告进度
OnProgressChanged(currProgress, totalProgress);
OnTransferTaskCompleted();
}
else
{
var currProgress = 100.0f;
var totalProgress = ((float)Position / (float)StoragePath.Length) * 100.0f;
// 报告进度
OnProgressChanged(currProgress, totalProgress);
OnTransferFileCompleted(info.Filename);
CurrentChunk.Filename = ID + "://" + TransferPath[Position];
CurrentChunk.ChunkID = 0;
var fileinfo = new FileInfo(StoragePath[Position]);
var fileSize = (int)fileinfo.Length;
var chunkCount = fileSize % ChunkSize == 0 ? fileSize / ChunkSize : fileSize / ChunkSize + 1;
CurrentChunk.FileSize = fileSize;
CurrentChunk.ChunkCount = chunkCount;
if (isStopped || IsPaused)
return;
// 继续上传剩余的文件
var upInfo = new ChunkInfo(CurrentChunk.Filename, CurrentChunk.FileSize, CurrentChunk.ChunkID, CurrentChunk.ChunkCount, CurrentChunk.ChunkSize);
appSession.SendRequestAsync(UPLOAD_FILE_INFO_REQUEST, upInfo);
}
}
}
private void UploadFileChunk(string filename)
{
if (filestream == null)
filestream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read, ChunkSize, false);
var buffer = BufferPool.Acquire();
var count = filestream.Read(buffer, 0, ChunkSize);
var data = new ArraySegment<byte>(buffer, 0, count);
var info = new AttachedChunkInfo(CurrentChunk.Filename, CurrentChunk.ChunkID, CurrentChunk.ChunkCount, CurrentChunk.ChunkSize, data);
appSession.SendRequestAsync(UPLOAD_CHUNK_INFO_REQUEST, info);
BufferPool.Release(buffer);
}
}
/// <summary>
/// 该类用于文件传输,可下载,也可上传。
/// </summary>
public static class FileTransfer
{
private const string DOWNLOAD_FILE_INFO_REPLY = "DOWNLOAD_FILE_INFO_REPLY";
private const string DOWNLOAD_CHUNK_INFO_REPLY = "DOWNLOAD_CHUNK_INFO_REPLY";
private const string UPLOAD_FILE_INFO_REPLY = "UPLOAD_FILE_INFO_REPLY";
private const string UPLOAD_CHUNK_INFO_REPLY = "UPLOAD_CHUNK_INFO_REPLY";
private static readonly string[] Delimiters = new string[] { "://" };
private static List<TransferTask> taskList = new List<TransferTask>();
/// <summary>
/// 初始化。
/// </summary>
public static void Initialize()
{
NetworkMessageDispatcher.AddListener(DOWNLOAD_FILE_INFO_REPLY, ProcessDownloadFileReplyInfo);
NetworkMessageDispatcher.AddListener(DOWNLOAD_CHUNK_INFO_REPLY, ProcessDownloadChunkReplyInfo);
NetworkMessageDispatcher.AddListener(UPLOAD_FILE_INFO_REPLY, ProcessUploadFileReplyInfo);
NetworkMessageDispatcher.AddListener(UPLOAD_CHUNK_INFO_REPLY, UploadChunkReplyInfo);
}
/// <summary>
/// 销毁资源。
/// </summary>
public static void Dispose()
{
NetworkMessageDispatcher.RemoveListener(DOWNLOAD_FILE_INFO_REPLY, ProcessDownloadFileReplyInfo);
NetworkMessageDispatcher.RemoveListener(DOWNLOAD_CHUNK_INFO_REPLY, ProcessDownloadChunkReplyInfo);
NetworkMessageDispatcher.RemoveListener(UPLOAD_FILE_INFO_REPLY, ProcessUploadFileReplyInfo);
NetworkMessageDispatcher.RemoveListener(UPLOAD_CHUNK_INFO_REPLY, UploadChunkReplyInfo);
}
/// <summary>
/// 开启一个传输任务。
/// </summary>
/// <param name="session">传输时使用的会话</param>
/// <param name="storagePath">本地的存储文件路径</param>
/// <param name="transferPath">要传输的文件路径</param>
/// <param name="type">传输类型</param>
public static TransferTask CreateTransferTask(IAppSession session, string[] storagePath, string[] transferPath, TransferType type)
{
var task = new TransferTask(session, storagePath, transferPath, type, taskList.Count);
taskList.Add(task);
return task;
}
public static bool DestroyTransferTask(TransferTask task)
{
if (task == null)
return true;
task.Dispose();
return taskList.Remove(task);
}
public static bool DestroyTransferTask(int taskID)
{
var index = taskList.FindIndex((task) => task.ID == taskID);
if (index < 0)
return false;
taskList[index].Dispose();
taskList.RemoveAt(index);
return true;
}
public static TransferTask GetTransferTask(int taskID)
{
var index = taskList.FindIndex((task) => task.ID == taskID);
if (index < 0)
return null;
return taskList[index];
}
public static List<TransferTask> GetTaskList()
{
return taskList;
}
public static void Clear()
{
for (var i = 0; i < taskList.Count; ++i)
taskList[i].Dispose();
taskList.Clear();
}
private static void ProcessDownloadFileReplyInfo(BinaryMessage message)
{
var info = message.Body.Deserialize<ChunkInfo>();
var array = info.Filename.Split(Delimiters, StringSplitOptions.None);
var taskID = int.Parse(array[0]);
var task = GetTransferTask(taskID);
if (task != null)
task.ProcessDownloadFileReplyInfo(ref info);
}
private static void ProcessDownloadChunkReplyInfo(BinaryMessage message)
{
var info = message.Body.Deserialize<AttachedChunkInfo>();
var array = info.Filename.Split(Delimiters, StringSplitOptions.None);
var taskID = int.Parse(array[0]);
var task = GetTransferTask(taskID);
if (task != null)
task.ProcessDownloadChunkReplyInfo(ref info);
}
private static void ProcessUploadFileReplyInfo(BinaryMessage message)
{
var info = message.Body.Deserialize<SimpleChunkInfo>();
var array = info.Filename.Split(Delimiters, StringSplitOptions.None);
var taskID = int.Parse(array[0]);
var task = GetTransferTask(taskID);
if (task != null)
task.ProcessUploadFileReplyInfo(ref info);
}
private static void UploadChunkReplyInfo(BinaryMessage message)
{
var info = message.Body.Deserialize<SimpleChunkInfo>();
var array = info.Filename.Split(Delimiters, StringSplitOptions.None);
var taskID = int.Parse(array[0]);
var task = GetTransferTask(taskID);
if (task != null)
task.ProcessUploadChunkReplyInfo(ref info);
}
}
}