网演高层钦州
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.
 
 
 

896 lines
34 KiB

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using AX.Network.Common;
using AX.Network.Protocols;
using AX.Serialization;
namespace AX.NetworkSystem
{
/// <summary>
/// 表示传输的文件信息。
/// </summary>
public struct TransferedFileInfo
{
/// <summary>
/// 表示传输的文件名。只包含文件名和扩展名,不包含路径。
/// </summary>
/// <remarks>文件名必须保证唯一。</remarks>
public string Filename;
/// <summary>
/// 表示传输的文件大小,最大支持 2G。
/// </summary>
public int FileSize;
/// <summary>
/// 表示文件附加信息。
/// </summary>
public ArraySegment<byte> Tag;
}
/// <summary>
/// 表示传输的文件块信息。
/// </summary>
public struct TransferedChunkInfo
{
/// <summary>
/// 表示文件块对应的文件。
/// </summary>
public string Filename;
/// <summary>
/// 表示文件块总数。
/// </summary>
public int ChunkCount;
/// <summary>
/// 表示文件块的 ID。
/// </summary>
public int ChunkId;
/// <summary>
/// 表示文件块划分的标准。
/// </summary>
public int ChunkSize;
/// <summary>
/// 表示文件块的数据。
/// </summary>
public ArraySegment<byte> Data;
}
/// <summary>
/// 表示传输文件块时的回复。
/// </summary>
public struct TransferedChunkInfoReply
{
/// <summary>
/// 表示文件名。
/// </summary>
public string Filename;
/// <summary>
/// 表示文件块 Id。
/// </summary>
public int ChunkId;
}
/// <summary>
/// 表示传输文件块时的请求。
/// </summary>
public struct TransferedChunkInfoRequest
{
/// <summary>
/// 表示文件名。
/// </summary>
public string Filename;
/// <summary>
/// 表示文件块 Id。
/// </summary>
public int ChunkId;
/// <summary>
/// 表示文件块划分的标准。
/// </summary>
public int ChunkSize;
}
/// <summary>
/// 表示错误消息。
/// </summary>
public struct ErrorInfo
{
/// <summary>
/// 表示出错的文件名。
/// </summary>
public string Filename;
/// <summary>
/// 表示错误码。
/// </summary>
public int ErrorCode;
/// <summary>
/// 表示错误详细信息。
/// </summary>
public string ErrorMessage;
}
/// <summary>
/// 表示文件传输过程中的状态。
/// </summary>
internal class TransferedFileStatus
{
/// <summary>
/// 表示传输文件会话。
/// </summary>
public IAppSession<BinaryProtocol, BinaryMessage> AppSession;
/// <summary>
/// 表示要传输的文件路径。相对路径和绝对路径均可。
/// </summary>
public string FullName;
/// <summary>
/// 表示文件名。只包含文件名和扩展名,不包含路径。
/// </summary>
public string Filename;
/// <summary>
/// 表示文件总共划分了多少块。
/// </summary>
public int TotalChunkCount;
/// <summary>
/// 表示当前传输到哪块了。
/// </summary>
public int CurrentChunkIndex;
/// <summary>
/// 表示当前已经传输了多少块了。
/// </summary>
public int TransferedChunkCount;
/// <summary>
/// 表示文件传输过程中的进度变化。
/// </summary>
public Action<string, float> ProgressChanged;
/// <summary>
/// 表示文件传输完毕事件。
/// </summary>
public Action<string> Completed;
/// <summary>
/// 表示文件传输时发生错误,当前只针对下载。
/// </summary>
public Action<ErrorInfo> Error;
}
public static class FileTransfer
{
internal class FileSyncStatus
{
public BitArray BitArray;
public ArraySegment<byte> Tag;
public int TotalChunkCount;
public int TransferedChunkCount;
}
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 DOWNLOAD_FILE_INFO_REPLY = "DOWNLOAD_FILE_INFO_REPLY";
private const string DOWNLOAD_CHUNK_INFO_REPLY = "DOWNLOAD_CHUNK_INFO_REPLY";
private const string DOWNLOAD_ERROR = "DOWNLOAD_ERROR";
private const string UPLOAD_FILE_INFO_REQUEST = "UPLOAD_FILE_INFO_REQUEST";
private const string UPLOAD_CHUNK_INFO_REQUEST = "UPLOAD_CHUNK_INFO_REQUEST";
private const string UPLOAD_FILE_INFO_REPLY = "UPLOAD_FILE_INFO_REPLY";
private const string UPLOAD_CHUNK_INFO_REPLY = "UPLOAD_CHUNK_INFO_REPLY";
private const string FORWARD_FILE_INFO_REQUEST = "FORWARD_FILE_INFO_REQUEST";
private const string FORWARD_CHUNK_INFO_REQUEST = "FORWARD_CHUNK_INFO_REQUEST";
private const string FORWARD_FILE_INFO_REPLY = "FORWARD_FILE_INFO_REPLY";
private const string FORWARD_CHUNK_INFO_REPLY = "FORWARD_CHUNK_INFO_REPLY";
private const string FORWARD_FILE_INFO_SYNC = "FORWARD_FILE_INFO_SYNC";
private const string FORWARD_CHUNK_INFO_SYNC = "FORWARD_CHUNK_INFO_SYNC";
private const string PULL_CHUNK_INFO_REQUEST = "PULL_CHUNK_INFO_REQUEST";
private const string PULL_CHUNK_INFO_REPLY = "PULL_CHUNK_INFO_REQUEST";
private const string PULL_FILE_INFO_REQUEST = "PULL_FILE_INFO_REQUEST";
private const string PULL_FILE_INFO_REPLY = "PULL_FILE_INFO_REPLY";
private const string SEND_FILE_SYNC = "SEND_FILE_SYNC";
private static string BaseTempFolder = Application.dataPath + Path.DirectorySeparatorChar + "Temp" + Path.DirectorySeparatorChar;
private static string BaseUploadFolder = Application.dataPath + Path.DirectorySeparatorChar + "Upload" + Path.DirectorySeparatorChar;
private static string TempFolder = BaseTempFolder;
private static string UploadFolder = BaseUploadFolder;
private static string DirectorySeparator = new string(new char[] { Path.DirectorySeparatorChar });
private const int ChunkSize = 65536 - 4096;
private const int TransferedChunkCount = 1024 * 1024 / ChunkSize;
private static readonly BufferPool BufferPool = new BufferPool(1, ChunkSize);
private static Dictionary<string, TransferedFileStatus> UploadContainer = new Dictionary<string, TransferedFileStatus>();
private static Dictionary<string, TransferedFileStatus> DownloadContainer = new Dictionary<string, TransferedFileStatus>();
private static Dictionary<string, TransferedFileStatus> ForwardContainer = new Dictionary<string, TransferedFileStatus>();
private static Dictionary<string, FileSyncStatus> FileSyncContainer = new Dictionary<string, FileSyncStatus>();
/// <summary>
/// 表示文件传输初始化。
/// </summary>
public static void Initialize()
{
NetworkMessageDispatcher.AddListener(DOWNLOAD_FILE_INFO_REPLY, OnDownloadFileInfoReply);
NetworkMessageDispatcher.AddListener(DOWNLOAD_CHUNK_INFO_REPLY, OnDownloadChunkInfoReply);
NetworkMessageDispatcher.AddListener(DOWNLOAD_ERROR, OnDownloadError);
NetworkMessageDispatcher.AddListener(UPLOAD_FILE_INFO_REPLY, OnUploadFileInfoReply);
NetworkMessageDispatcher.AddListener(UPLOAD_CHUNK_INFO_REPLY, OnUploadChunkInfoReply);
NetworkMessageDispatcher.AddListener(FORWARD_FILE_INFO_REPLY, OnForwardFileInfoReply);
NetworkMessageDispatcher.AddListener(FORWARD_CHUNK_INFO_REPLY, OnForwardChunkInfoReply);
NetworkMessageDispatcher.AddListener(FORWARD_FILE_INFO_SYNC, OnForwardFileInfoSync);
NetworkMessageDispatcher.AddListener(FORWARD_CHUNK_INFO_SYNC, OnForwardChunkInfoSync);
NetworkMessageDispatcher.AddListener(PULL_FILE_INFO_REPLY, OnPullFileInfoReply);
NetworkMessageDispatcher.AddListener(PULL_CHUNK_INFO_REPLY, OnPullChunkInfoReply);
}
public static void Dispose()
{
NetworkMessageDispatcher.RemoveListener(DOWNLOAD_FILE_INFO_REPLY, OnDownloadFileInfoReply);
NetworkMessageDispatcher.RemoveListener(DOWNLOAD_CHUNK_INFO_REPLY, OnDownloadChunkInfoReply);
NetworkMessageDispatcher.RemoveListener(DOWNLOAD_ERROR, OnDownloadError);
NetworkMessageDispatcher.RemoveListener(UPLOAD_FILE_INFO_REPLY, OnUploadFileInfoReply);
NetworkMessageDispatcher.RemoveListener(UPLOAD_CHUNK_INFO_REPLY, OnUploadChunkInfoReply);
NetworkMessageDispatcher.RemoveListener(FORWARD_FILE_INFO_REPLY, OnForwardFileInfoReply);
NetworkMessageDispatcher.RemoveListener(FORWARD_CHUNK_INFO_REPLY, OnForwardChunkInfoReply);
NetworkMessageDispatcher.RemoveListener(FORWARD_FILE_INFO_SYNC, OnForwardFileInfoSync);
NetworkMessageDispatcher.RemoveListener(FORWARD_CHUNK_INFO_SYNC, OnForwardChunkInfoSync);
NetworkMessageDispatcher.RemoveListener(PULL_FILE_INFO_REPLY, OnPullFileInfoReply);
NetworkMessageDispatcher.RemoveListener(PULL_CHUNK_INFO_REPLY, OnPullChunkInfoReply);
}
/// <summary>
/// 设置文件下载时的临时目录。
/// </summary>
/// <param name="temp">用户指定的临时目录</param>
/// <remarks>注意:最好在登录成功之后进行设置。用户指定的临时完整路径是 Application.dataPath/Temp/temp/。</remarks>
public static void SetTempPath(string temp)
{
if (temp.EndsWith(DirectorySeparator))
TempFolder = BaseTempFolder + temp;
else
TempFolder = BaseTempFolder + temp + DirectorySeparator;
}
/// <summary>
/// 获得文件下载时的临时目录。
/// </summary>
public static string GetTempPath()
{
return TempFolder;
}
/// <summary>
/// 设置文件上传目录。
/// </summary>
/// <param name="upload">用户指定的文件上传目录</param>
/// <remarks>注意:最好在登录成功之后进行设置。</remarks>
public static void SetUploadPath(string upload)
{
if (upload.EndsWith(DirectorySeparator))
UploadFolder = upload;
else
UploadFolder = upload + DirectorySeparator;
}
/// <summary>
/// 获得文件上传目录。
/// </summary>
public static string GetUploadPath()
{
return UploadFolder;
}
private static void OnPullFileInfoReply(BinaryMessage message)
{
var info = message.Body.Deserialize<TransferedFileInfo>();
var status = default(FileSyncStatus);
if (!FileSyncContainer.TryGetValue(info.Filename, out status))
{
status = new FileSyncStatus();
status.Tag = info.Tag;
FileSyncContainer.Add(info.Filename, status);
}
else
status.Tag = info.Tag;
}
private static void OnPullChunkInfoReply(BinaryMessage message)
{
var status = default(FileSyncStatus);
var info = message.Body.Deserialize<TransferedChunkInfo>();
using (var stream = new FileStream(TempFolder + info.Filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
var position = info.ChunkId * info.ChunkSize;
stream.Seek(position, SeekOrigin.Begin);
stream.Write(info.Data.Array, info.Data.Offset, info.Data.Count);
stream.Flush();
}
status.BitArray[info.ChunkId] = true;
++status.TransferedChunkCount;
// 确实同步完成
if (status.TransferedChunkCount == info.ChunkCount)
{
FileSyncContainer.Remove(info.Filename);
ForwardContainer.Remove(info.Filename);
var tag = status.Tag.Deserialize<KeyValuePair<string, ArraySegment<byte>>>();
NetworkMessageDispatcher.SendMessage(tag.Key, tag.Value, TempFolder + info.Filename);
}
else
{
// 拉取未完成的文件块
var chunkId = GetChunkId(status.BitArray);
if (chunkId > 0)
{
var request = new TransferedChunkInfoRequest();
request.Filename = info.Filename;
request.ChunkId = chunkId;
request.ChunkSize = ChunkSize;
var session = ForwardContainer[info.Filename].AppSession;
session.SendRequestAsync(PULL_CHUNK_INFO_REQUEST, request);
}
}
}
private static void OnForwardChunkInfoSync(BinaryMessage message)
{
var info = message.Body.Deserialize<TransferedChunkInfo>();
var status = default(FileSyncStatus);
if (!FileSyncContainer.TryGetValue(info.Filename, out status))
{
status = new FileSyncStatus();
FileSyncContainer.Add(info.Filename, status);
// 拉取文件信息
var session = ForwardContainer[info.Filename].AppSession;
session.SendRequestAsync(PULL_FILE_INFO_REQUEST, info.Filename);
}
if (status.BitArray == null)
{
status.BitArray = new BitArray(info.ChunkCount);
status.TotalChunkCount = info.ChunkCount;
}
using (var stream = new FileStream(TempFolder + info.Filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
var position = info.ChunkId * info.ChunkSize;
stream.Seek(position, SeekOrigin.Begin);
stream.Write(info.Data.Array, info.Data.Offset, info.Data.Count);
stream.Flush();
}
status.BitArray[info.ChunkId] = true;
status.TransferedChunkCount += 1;
// 同步到了最后一个文件块
if (info.ChunkId == info.ChunkCount - 1)
{
// 确实同步完成
if (status.TransferedChunkCount == info.ChunkCount)
{
FileSyncContainer.Remove(info.Filename);
ForwardContainer.Remove(info.Filename);
var tag = status.Tag.Deserialize<KeyValuePair<string, ArraySegment<byte>>>();
NetworkMessageDispatcher.SendMessage(tag.Key, tag.Value, TempFolder + info.Filename);
}
else
{
// 拉取未完成的文件块
var chunkId = GetChunkId(status.BitArray);
if (chunkId > 0)
{
var request = new TransferedChunkInfoRequest();
request.Filename = info.Filename;
request.ChunkId = chunkId;
request.ChunkSize = ChunkSize;
var session = ForwardContainer[info.Filename].AppSession;
session.SendRequestAsync(PULL_CHUNK_INFO_REQUEST, request);
}
}
}
}
private static void OnForwardFileInfoSync(BinaryMessage message)
{
var info = message.Body.Deserialize<TransferedFileInfo>();
var fullname = TempFolder + info.Filename;
if (!Directory.Exists(TempFolder))
Directory.CreateDirectory(TempFolder);
if (!File.Exists(fullname))
{
using (var stream = new FileStream(fullname, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
{
stream.SetLength(info.FileSize);
stream.Flush();
}
}
var status = default(FileSyncStatus);
if (!FileSyncContainer.TryGetValue(info.Filename, out status))
{
status = new FileSyncStatus();
status.Tag = info.Tag;
FileSyncContainer.Add(info.Filename, status);
}
}
private static void OnForwardChunkInfoReply(BinaryMessage message)
{
// 传输文件块
var reply = message.Body.Deserialize<TransferedChunkInfoReply>();
var status = default(TransferedFileStatus);
if (!ForwardContainer.TryGetValue(reply.Filename, out status))
return;
++status.TransferedChunkCount;
// 表示文件上传成功
if (status.TransferedChunkCount == status.TotalChunkCount)
{
if (status.ProgressChanged != null)
status.ProgressChanged(status.FullName, 100.0f);
if (status.Completed != null)
status.Completed(status.FullName);
//ForwardContainer.Remove(status.Filename);
}
else
{
var progress = ((float)status.TransferedChunkCount / status.TotalChunkCount) * 100.0f;
if (status.ProgressChanged != null)
status.ProgressChanged(status.FullName, progress);
++status.CurrentChunkIndex;
// 继续上传文件
if (status.CurrentChunkIndex < status.TotalChunkCount)
ForwardFileChunkAsync(status, status.CurrentChunkIndex);
}
}
private static void OnForwardFileInfoReply(BinaryMessage message)
{
var pair = message.Body.Deserialize<KeyValuePair<string, ArraySegment<byte>>>();
var filename = pair.Key;
var body = pair.Value.Deserialize<KeyValuePair<string, ArraySegment<byte>>>();
// 通知客户端收到回复了
NetworkMessageDispatcher.SendMessage(body.Key, body.Value, filename);
// 开始真正上传文件
var status = ForwardContainer[filename];
var count = Math.Min(status.TotalChunkCount, TransferedChunkCount);
status.CurrentChunkIndex = count - 1;
for (var i = 0; i < count; ++i)
ForwardFileChunkAsync(status, i);
}
private static void OnUploadChunkInfoReply(BinaryMessage message)
{
// 传输文件块
var reply = message.Body.Deserialize<TransferedChunkInfoReply>();
var status = default(TransferedFileStatus);
if (!UploadContainer.TryGetValue(reply.Filename, out status))
return;
++status.TransferedChunkCount;
// 表示文件上传成功
if (status.TransferedChunkCount == status.TotalChunkCount)
{
if (status.ProgressChanged != null)
status.ProgressChanged(status.FullName, 100.0f);
if (status.Completed != null)
status.Completed(status.FullName);
UploadContainer.Remove(status.Filename);
}
else
{
var progress = ((float)status.TransferedChunkCount / status.TotalChunkCount) * 100.0f;
if (status.ProgressChanged != null)
status.ProgressChanged(status.FullName, progress);
++status.CurrentChunkIndex;
// 继续上传文件
if (status.CurrentChunkIndex < status.TotalChunkCount)
UploadFileChunkAsync(status, status.CurrentChunkIndex);
}
}
private static void OnUploadFileInfoReply(BinaryMessage message)
{
// 开始真正上传文件
var filename = message.Body.Deserialize<string>();
var status = UploadContainer[filename];
var count = Math.Min(status.TotalChunkCount, TransferedChunkCount);
status.CurrentChunkIndex = count - 1;
for (var i = 0; i < count; ++i)
UploadFileChunkAsync(status, i);
}
private static void OnDownloadError(BinaryMessage message)
{
var error = message.Body.Deserialize<ErrorInfo>();
var status = DownloadContainer[error.Filename];
if (status.Error != null)
status.Error(error);
DownloadContainer.Remove(error.Filename);
}
private static void OnDownloadChunkInfoReply(BinaryMessage message)
{
var info = message.Body.Deserialize<TransferedChunkInfo>();
var status = DownloadContainer[info.Filename];
using (var stream = new FileStream(status.FullName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
{
stream.Seek(info.ChunkId * info.ChunkSize, SeekOrigin.Begin);
stream.Write(info.Data.Array, info.Data.Offset, info.Data.Count);
stream.Flush();
}
status.TotalChunkCount = info.ChunkCount;
++status.TransferedChunkCount;
// 表示文件下载成功
if (status.TransferedChunkCount == status.TotalChunkCount)
{
if (status.ProgressChanged != null)
status.ProgressChanged(status.FullName, 100.0f);
if (status.Completed != null)
status.Completed(status.FullName);
DownloadContainer.Remove(status.Filename);
}
else
{
var progress = ((float)status.TransferedChunkCount / status.TotalChunkCount) * 100.0f;
if (status.ProgressChanged != null)
status.ProgressChanged(status.FullName, progress);
// 继续下载文件
if (status.CurrentChunkIndex == 0)
{
var count = Math.Min(info.ChunkCount, TransferedChunkCount);
for (var i = 1; i < count; ++i)
DownloadFileChunkAsync(status, i);
status.CurrentChunkIndex += count;
}
else
{
if (status.CurrentChunkIndex < status.TotalChunkCount)
{
DownloadFileChunkAsync(status, status.CurrentChunkIndex);
++status.CurrentChunkIndex;
}
}
}
}
private static void OnDownloadFileInfoReply(BinaryMessage message)
{
// 开始真正下载文件
var info = message.Body.Deserialize<TransferedFileInfo>();
var status = DownloadContainer[info.Filename];
using (var stream = new FileStream(status.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
stream.SetLength(info.FileSize);
stream.Flush();
}
status.CurrentChunkIndex = 0;
// 下载第一块
DownloadFileChunkAsync(status, 0);
}
private static void UploadFileChunkAsync(TransferedFileStatus status, int chunkId)
{
var buffer = BufferPool.Acquire();
using (var stream = new FileStream(status.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
stream.Seek(chunkId * ChunkSize, SeekOrigin.Begin);
var readCount = stream.Read(buffer, 0, ChunkSize);
var data = new ArraySegment<byte>(buffer, 0, readCount);
var info = new TransferedChunkInfo();
info.Filename = status.Filename;
info.ChunkCount = status.TotalChunkCount;
info.ChunkId = chunkId;
info.ChunkSize = ChunkSize;
info.Data = data;
status.AppSession.SendRequestAsync(UPLOAD_CHUNK_INFO_REQUEST, info);
}
BufferPool.Release(buffer);
}
private static void ForwardFileChunkAsync(TransferedFileStatus status, int chunkId)
{
var buffer = BufferPool.Acquire();
using (var stream = new FileStream(status.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
stream.Seek(chunkId * ChunkSize, SeekOrigin.Begin);
var readCount = stream.Read(buffer, 0, ChunkSize);
var data = new ArraySegment<byte>(buffer, 0, readCount);
var info = new TransferedChunkInfo();
info.Filename = status.Filename;
info.ChunkCount = status.TotalChunkCount;
info.ChunkId = chunkId;
info.ChunkSize = ChunkSize;
info.Data = data;
status.AppSession.SendRequestAsync(FORWARD_CHUNK_INFO_REQUEST, info);
}
BufferPool.Release(buffer);
}
private static void DownloadFileChunkAsync(TransferedFileStatus status, int chunkId)
{
var request = new TransferedChunkInfoRequest();
request.Filename = status.Filename;
request.ChunkId = chunkId;
status.AppSession.SendRequestAsync(DOWNLOAD_CHUNK_INFO_REQUEST, request);
}
// 获得未完成的文件块索引
private static int GetChunkId(BitArray array)
{
for (var i = 0; i < array.Length; ++i)
if (!array[i])
return i;
return -1;
}
public static void UploadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string filename)
{
UploadFileAsync(session, filename, null, null);
}
public static void UploadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string filename, Action<string, float> progress)
{
UploadFileAsync(session, filename, progress, null);
}
public static void UploadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string filename, Action<string> completed)
{
UploadFileAsync(session, filename, null, completed);
}
/// <summary>
/// 异步上传文件。
/// </summary>
public static void UploadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string filename, Action<string, float> progress, Action<string> completed)
{
var fullname = UploadFolder + filename;
if (!File.Exists(fullname))
throw new ArgumentException("filename");
var fileinfo = new FileInfo(fullname);
var fileSize = (int)fileinfo.Length;
var chunkCount = fileSize % ChunkSize == 0 ? fileSize / ChunkSize : fileSize / ChunkSize + 1;
var info = new TransferedFileInfo();
info.Filename = filename;
info.FileSize = fileSize;
var status = new TransferedFileStatus();
status.AppSession = session;
status.FullName = fullname;
status.Filename = filename;
status.TotalChunkCount = chunkCount;
status.ProgressChanged = progress;
status.Completed = completed;
UploadContainer.Add(status.Filename, status);
// 发送上传文件请求
session.SendRequestAsync(UPLOAD_FILE_INFO_REQUEST, info);
}
public static void SendFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string header, string filename)
{
SendFileAsync(session, header, filename, (Action<string, float>)null);
}
public static void SendFileAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, string header, T body, string filename)
{
SendFileAsync(session, header, body, filename, null, null);
}
public static void SendFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string header, string filename, Action<string, float> progress)
{
SendFileAsync(session, header, filename, progress, null);
}
public static void SendFileAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, string header, T body, string filename, Action<string, float> progress)
{
SendFileAsync(session, header, body, filename, progress, null);
}
public static void SendFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string header, string filename, Action<string, float> progress, Action<string> completed)
{
var pair = new KeyValuePair<string, ArraySegment<byte>>(header, default(ArraySegment<byte>));
SendFileAsync(session, filename, pair, progress, completed);
}
public static void SendFileAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, string header, T body, string filename, Action<string, float> progress, Action<string> completed)
{
var buffer = BufferPool.Acquire();
var segment = body.Serialize();
Buffer.BlockCopy(segment.Array, segment.Offset, buffer, 0, segment.Count);
var rawBody = new ArraySegment<byte>(buffer, 0, segment.Count);
var pair = new KeyValuePair<string, ArraySegment<byte>>(header, rawBody);
SendFileAsync(session, filename, pair, progress, completed);
BufferPool.Release(buffer);
}
/// <summary>
/// 异步转发文件。
/// </summary>
/// <remarks>注意,调用此方法传输的文件应该都是小文件(不大于 1M 的文件)。传输大文件可能会比较低效。</remarks>
public static void SendFileAsync<T>(this IAppSession<BinaryProtocol, BinaryMessage> session, string filename, T tag, Action<string, float> progress, Action<string> completed)
{
if (!File.Exists(filename))
throw new ArgumentException("filename");
var fileinfo = new FileInfo(filename);
var fileSize = (int)fileinfo.Length;
var chunkCount = fileSize % ChunkSize == 0 ? fileSize / ChunkSize : fileSize / ChunkSize + 1;
var info = new TransferedFileInfo();
info.Filename = fileinfo.Name;
info.FileSize = fileSize;
var buffer = BufferPool.Acquire();
var segment = tag.Serialize();
Buffer.BlockCopy(segment.Array, segment.Offset, buffer, 0, segment.Count);
info.Tag = new ArraySegment<byte>(buffer, 0, segment.Count);
var status = new TransferedFileStatus();
status.AppSession = session;
status.FullName = filename;
status.Filename = fileinfo.Name;
status.TotalChunkCount = chunkCount;
status.ProgressChanged = progress;
status.Completed = completed;
ForwardContainer.Add(status.Filename, status);
// 发送转发文件请求
session.SendRequestAsync(FORWARD_FILE_INFO_REQUEST, info);
BufferPool.Release(buffer);
}
public static void DownloadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string filename)
{
DownloadFileAsync(session, filename, null, null);
}
public static void DownloadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string filename, Action<string> completed)
{
DownloadFileAsync(session, filename, null, completed);
}
public static void DownloadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string filename, Action<string, float> progress)
{
DownloadFileAsync(session, filename, progress, null);
}
/// <summary>
/// 异步下载文件。
/// </summary>
public static void DownloadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string fullname, Action<string, float> progress, Action<string> completed)
{
DownloadFileAsync(session, fullname, progress, completed, null);
}
/// <summary>
/// 异步下载文件。
/// </summary>
public static void DownloadFileAsync(this IAppSession<BinaryProtocol, BinaryMessage> session, string fullname, Action<string, float> progress, Action<string> completed, Action<ErrorInfo> error)
{
if (string.IsNullOrEmpty(fullname))
throw new ArgumentException("fullname 不能为空!");
var actualName = TempFolder + fullname;
var directory = Path.GetDirectoryName(TempFolder + fullname);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
var status = new TransferedFileStatus();
status.AppSession = session;
status.FullName = actualName;
status.Filename = fullname;
status.ProgressChanged = progress;
status.Completed = completed;
status.Error = error;
DownloadContainer.Add(status.Filename, status);
var info = new TransferedFileInfo();
info.Filename = fullname;
info.FileSize = 0;
// 发送下载文件请求
session.SendRequestAsync(DOWNLOAD_FILE_INFO_REQUEST, info);
}
}
}